2020数据库面试题

时间:2022-07-22
本文章向大家介绍2020数据库面试题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

索引

1. 那你能说说什么是索引吗?

索引其实是一种数据结构,能够帮助我们快速的检索数据库中的数据

2. 那么索引具体采用的哪种数据结构呢?

常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,我们使用的是InnoDB引擎,默认的是B+树

3. 既然你提到InnoDB使用的B+ 树的索引模型,那么你知道为什么采用B+ 树吗?这和Hash索引比较起来有什么优缺点吗?

B+ Tree索引和Hash索引区别?

哈希索引适合等值查询,但是无法进行范围查询

哈希索引没办法利用索引完成排序

哈希索引不支持多列联合索引的最左匹配规则

如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题

4. B+ Tree的叶子节点都可以存哪些东西吗?

InnoDB的B+ Tree可能存储的是整行数据,也有可能是主键的值

在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。

而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引

5. 聚簇索引和非聚簇索引,在查询数据的时候有区别吗?

聚簇索引查询会更快

因为主键索引树的叶子节点直接就是我们要查询的整行数据了。而非主键索引的叶子节点是主键的值,查到主键的值以后,还需要再通过主键的值再进行一次回表查询

6. 非主键索引一定会回表查询多次吗?

通过覆盖索引也可以只查询一次

覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。

7. 创建索引的时候都会考虑哪些因素呢?

一般对于查询概率比较高,经常作为where条件的字段设置索引

8. 那你们在创建联合索引的时候,需要做联合索引多个字段之间顺序你们是如何选择的呢?

我们把识别度最高的字段放到最前面

在创建多列索引时,我们根据业务需求,where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则

9. 有什么手段可以知道有没有走索引查询呢?

通过explain查看sql语句的执行计划,通过执行计划来分析索引使用情况

10. 那什么情况下会发生明明创建了索引,但是执行的时候并没有通过索引呢?

一条SQL语句的查询,可以有不同的执行方案,至于最终选择哪种方案,需要通过优化器进行选择,选择执行成本最低的方案。 在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。 这个成本最低的方案就是所谓的执行计划。优化过程大致如下: 1、根据搜索条件,找出所有可能使用的索引 2、计算全表扫描的代价 3、计算使用不同索引执行查询的代价 4、对比各种执行方案的代价,找出成本最低的那一个

使用不等于查询, 列参与了数学运算或者函数 在字符串like时左边是通配符.类似于'%aaa'. 当mysql分析全表扫描比使用索引快的时候不使用索引. 当使用联合索引,前面一个条件为范围查询,后面的即使符合最左前缀原则,也无法使用索引.

事务

1. 什么是事务?

事务是一系列的操作,他们要符合ACID特性.最常见的理解就是:事务中的操作要么全部成功,要么全部失败.但是只是这样还不够的.

2. ACID是什么?

A=Atomicity

原子性,就是上面说的,要么全部成功,要么全部失败.不可能只执行一部分操作.

C=Consistency

系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态.

I=Isolation

隔离性: 通常来说:一个事务在完全提交之前,对其他事务是不可见的.注意前面的通常来说加了红色,意味着有例外情况.

D=Durability

持久性,一旦事务提交,那么就永远是这样子了,哪怕系统崩溃也不会影响到这个事务的结果.

3. 同时有多个事务在进行会怎么样呢?

多事务的并发进行一般会造成以下几个问题:

脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。

丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。

不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

4. 不可重复读和幻读的区别?

不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。

5. 怎么解决这些问题呢?MySQL的事务隔离级别了解吗?

MySQL的四种隔离级别如下:

未提交读(READ UNCOMMITTED) 这就是上面所说的例外情况了,这个隔离级别下,其他事务可以看到本事务没有提交的部分修改.因此会造成脏读的问题(读取到了其他事务未提交的部分,而之后该事务进行了回滚).

这个级别的性能没有足够大的优势,但是又有很多的问题,因此很少使用.

已提交读(READ COMMITTED) 其他事务只能读取到本事务已经提交的部分.这个隔离级别有 不可重复读的问题,在同一个事务内的两次读取,拿到的结果竟然不一样,因为另外一个事务对数据进行了修改.

REPEATABLE READ(可重复读) 可重复读隔离级别解决了上面不可重复读的问题(看名字也知道),但是仍然有一个新问题,就是 幻读,当你读取id> 10 的数据行时,对涉及到的所有行加上了读锁,此时例外一个事务新插入了一条id=11的数据,因为是新插入的,所以不会触发上面的锁的排斥,那么进行本事务进行下一次的查询时会发现有一条id=11的数据,而上次的查询操作并没有获取到,再进行插入就会有主键冲突的问题.

SERIALIZABLE(可串行化) 这是最高的隔离级别,可以解决上面提到的所有问题,因为他强制将所以的操作串行执行,这会导致并发性能极速下降,因此也不是很常用.

6. Innodb使用的是哪种隔离级别呢?

InnoDB默认使用的是可重复读隔离级别.

7. Mysql三大范式

第一范式: 无重复的列

第二范式: 实体的属性完全依赖于关键字,不能存在仅仅依赖于主键的部分属性

第三范式: 表属性不包含其他表的非主属性

8. innodb有多少种日志?

redo和undo

9. 日志的存放形式?

redo:在页修改的时候,先写到 redo log buffer 里面, 然后写到 redo log 的文件系统缓存里面(fwrite),然后再同步到磁盘文件( fsync)。 Undo:在 MySQL5.5 之前, undo 只能存放在 ibdata文件里面, 5.6 之后,可以通过设置 innodb_undo_tablespaces 参数把 undo log 存放在 ibdata之外。

10. 事务是如何通过日志来实现的,说得越深入越好

基本流程如下: 因为事务在修改页时,要先记 undo,在记 undo 之前要记 undo 的 redo, 然后修改数据页,再记数据页修改的 redo。 Redo(里面包括 undo 的修改) 一定要比数据页先持久化到磁盘。 当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的 状态,崩溃恢复时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo把该事务的修改回滚到事务开始之前。 如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。

11. MVCC 谈谈你的理解?

mvcc 并不能完全解决幻读

mvcc 通过版本的方式,去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读

当前读

像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁

快照读

像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本

12. MVCC能解决什么问题,好处是?

数据库并发场景有三种,分别为:

读-读:不存在任何问题,也不需要并发控制

读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读

写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 所以MVCC可以为数据库解决以下问题

在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能 同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题

13. MVCC 的实现原理

它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的。

在每一行数据中额外保存两个隐藏的列:当前行创建时的版本号和删除时的版本号。这里的版本号并不是实际的时间值,而是系统版本号。每开始新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询每行记录的版本号进行比较。

每个事务又有自己的版本号,这样事务内执行CRUD操作时,就通过版本号的比较来达到数据版本控制的目的。

1. Mysql 支持锁结构?

  1. 表级锁,开销小,加锁快,不会出现死锁,锁定的粒度大,冲突概率高,并发度最低
  2. 行级锁,开销小,加锁慢,会出现死锁,锁定粒度小,冲突概率最低,并发度最高
  3. 页面锁,开销和加锁处于表锁和行锁之间,会出现死锁,锁粒度基于表和行之间,并发一般

2. InnoDB的行锁模式及加锁方法

InnoDB实现了以下两种类型的行锁。

共享锁(s):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁。

另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。

意向共享锁(IS):事务打算给数据行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

当前锁模式和请求锁模式

X

IX

S

IS

X

冲突

冲突

冲突

冲突

IX

冲突

兼容

冲突

兼容

S

冲突

冲突

兼容

兼容

IS

冲突

兼容

兼容

兼容

3.InnoDb 什么时候使用行锁?

InnoDB行锁是通过索引上的索引项来实现的,这一点MySQL与Oracle不同,后者是通过在数据中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味者:

只有通过索引条件检索数据,InnoDB才会使用行级锁,否则,InnoDB将使用表锁!

4. Innodb 什么时候使用表锁?

下面的几种情况:

第一种情况是:事务需要更新大部分或全部数据,表又比较大,如果使用默认的行锁,不仅这个事务执行效率低,而且可能造成其他事务长时间锁等待和锁冲突,这种情况下可以考虑使用表锁来提高该事务的执行速度。

第二种情况是:事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚。这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁、减少数据库因事务回滚带来的开销。

5. InnoDb发生死锁后?

发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并退回,另一个事务获得锁,继续完成事务。但在涉及外部锁,或涉及锁的情况下,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决。

6. Innodb锁的特点

(1)InnoDB的行销是基于索引实现的,如果不通过索引访问数据,InnoDB会使用表锁。 (2)InnoDB间隙锁机制,以及InnoDB使用间隙锁的原因。 (3)在不同的隔离级别下,InnoDB的锁机制和一致性读策略不同。 (4)MySQL的恢复和复制对InnoDB锁机制和一致性读策略也有较大影响。 (5)锁冲突甚至死锁很难完全避免。尽量使用较低的隔离级别,选择合理的事务大小,小事务发生锁冲突的几率也更小。给记录集显示加锁时,最好一次性请求足够级别的锁。

分库分表

1. 横向分表和纵向分表,可以分别举一个适合他们的例子吗?

横向分表是按行分表.假设我们有一张用户表,主键是自增ID且同时是用户的ID.数据量较大,有1亿多条,那么此时放在一张表里的查询效果就不太理想.我们可以根据主键ID进行分表,无论是按尾号分,或者按ID的区间分都是可以的. 假设按照尾号0-99分为100个表,那么每张表中的数据就仅有100w.这时的查询效率无疑是可以满足要求的.

纵向分表是按列分表.假设我们现在有一张文章表.包含字段id-摘要-内容.而系统中的展示形式是刷新出一个列表,列表中仅包含标题和摘要,当用户点击某篇文章进入详情时才需要正文内容.此时,如果数据量大,将内容这个很大且不经常使用的列放在一起会拖慢原表的查询速度.我们可以将上面的表分为两张.id-摘要,id-内容.当用户点击详情,那主键再来取一次内容即可.而增加的存储量只是很小的主键字段.代价很小.

2. sharding-jdbc 和 myCat 的区别?

sharding-jdbc 是当当开源的,属于client层的解决方案.支持分库分表,支持读写分离,分布式id生成,柔性事务,不需要单独部署,运维的成本低,性能很高,但是各个系统都需要耦合sharding-jdbc的依赖.

myCat 这种proxy层的解决方案,需要部署,运维成本很高,但是对于各个项目是透明的,耦合性低.主要采用拦截的方法。

3. mySql主从复制的原理

  1. 主库将变更的操作写入bin-log日志中(增,删,改操作).
  2. 从库中的I/O线程将主库的bin-log拷贝到本地,写入relay-log(中继日志中)
  3. 从库的SQL线程从中继日志中读取bin-log然后再在本地执行一遍SQL,保证从库和主库数据的一致性.

4. 主从延迟问题产生的原因

dump log的操作是并发的多线程操作,但是从库的I/O和SQL线程是单线程的操作,(5.6.x后I/O可以多线程操作),但是SQL线程的执行一定是串行的执行,这也就导致了主从复制的延时问题的原因.

SQL的执行是串行化的所以导致,在高并发的情况下,从库的数据比主库慢一些,是有延时的.基本上写1000/s 会产生十几毫秒的延时问题,2000/s 会出现几十毫秒的延时.

5. 主从复制的数据丢失问题

如果主库突然宕机,然后数据还没有同步到从库,那么数据在从库上是没有的可以存在数据的丢失.

  1. 半同步复制 - 解决数据丢失问题

半同步复制,semi-sync复制,指的是主库写入bin-log日志后,就会强制此时立即同步数据库,所有从库可以将bin-log写入自己本地的relay-log,只有有一个从库写成功,就会给主库返回一个ack,主库接受到ack才会认为写操作完成,否则将进行回滚从新写入.

6. mysql主从同步延时问题

使用下面的语句可以看到从库落后主库的秒数

show status,Seconds_Behind_Master

解决方案:

  1. 分库:将主库拆分为4个主库,减少主库的写压力,此时主从延时可以忽略.
  2. mysql的并行复制,多个库并行复制,如果说某个库的写入并发就是特别高,单库写并发达到了2000/s,并行复制还是没意义。如果就一个表达到2000/s 其他都才10/s 那么并行复制没什么意义.
  3. 在插入数据后不进行查询直接更新,这样的更新就是在主库进行

Xtrabackup 进行mySql热备份

innodb的事务与日志的实现方式

1、有多少种日志

redo和undo

2、日志的存放形式

redo:在页修改的时候,先写到 redo log buffer 里面, 然后写到 redo log 的文件系统缓存里面(fwrite),然后再同步到磁盘文件( fsync)。 Undo:在 MySQL5.5 之前, undo 只能存放在 ibdata文件里面, 5.6 之后,可以通过设置 innodb_undo_tablespaces 参数把 undo log 存放在 ibdata之外。

3、事务是如何通过日志来实现的,说得越深入越好

基本流程如下: 因为事务在修改页时,要先记 undo,在记 undo 之前要记 undo 的 redo, 然后修改数据页,再记数据页修改的 redo。 Redo(里面包括 undo 的修改) 一定要比数据页先持久化到磁盘。 当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的 状态,崩溃恢复时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo把该事务的修改回滚到事务开始之前。 如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。

mysql不使用hash表做索引是因为,hash表可以快速定位数据,但是不支持范围查询 完全平衡二叉树,可能比较慢,可以支持范围查询

索引需要作为文件存储起来,B+ 树的磁盘IO较少

一个节点存储多少个元素? 固态硬盘使用电进行读写,而机械硬盘使用机械运动

一个节点为一个页的大小 1,2,4 倍数页

mysql 一个节点16k

叶子节点存储的数据

InnoDB 中辅助索引比主键索引花更多的时间

千万条数据,只用3次磁盘IO

innodb 锁

InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

InnoDB存储引擎的锁的算法有三种:

Record lock:单个行记录上的锁

Gap lock:间隙锁,锁定一个范围,不包括记录本身

Next-key lock:record+gap 锁定一个范围,包含记录本身

innodb对于行的查询使用next-key lock

Next-locking keying为了解决Phantom Problem幻读问题

当查询的索引含有唯一属性时,将next-key lock降级为record key

Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生

有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1

主从复制的原理

  1. 主库将变更的操作写入bin-log日志中(增,删,改操作).
  2. 从库中的I/O线程将主库的bin-log拷贝到本地,写入relay-log(中继日志中)
  3. 从库的SQL线程从中继日志中读取bin-log然后再在本地执行一遍SQL,保证从库和主库数据的一致性.

主从延迟问题产生的原因

dump log的操作是并发的多线程操作,但是从库的I/O和SQL线程是单线程的操作,(5.6.x后I/O可以多线程操作),但是SQL线程的执行一定是串行的执行,这也就导致了主从复制的延时问题的原因.

SQL的执行是串行化的所以导致,在高并发的情况下,从库的数据比主库慢一些,是有延时的.基本上写1000/s 会产生十几毫秒的延时问题,2000/s 会出现几十毫秒的延时.

主从复制的数据丢失问题

如果主库突然宕机,然后数据还没有同步到从库,那么数据在从库上是没有的可以存在数据的丢失.

  1. 半同步复制 - 解决数据丢失问题

半同步复制,semi-sync复制,指的是主库写入bin-log日志后,就会强制此时立即同步数据库,所有从库可以将bin-log写入自己本地的relay-log,只有有一个从库写成功,就会给主库返回一个ack,主库接受到ack才会认为写操作完成,否则将进行回滚从新写入.

mysql主从同步延时问题

使用下面的语句可以看到从库落后主库的秒数

show status,Seconds_Behind_Master

解决方案:

  1. 分库:将主库拆分为4个主库,减少主库的写压力,此时主从延时可以忽略.
  2. mysql的并行复制,多个库并行复制,如果说某个库的写入并发就是特别高,单库写并发达到了2000/s,并行复制还是没意义。如果就一个表达到2000/s 其他都才10/s 那么并行复制没什么意义.
  3. 在插入数据后不进行查询直接更新,这样的更新就是在主库进行

MySql乐观锁悲观锁

悲观锁

悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能失败的,因此要先确保获取锁成功再进行业务操作。通常所说的“一锁二查三更新”即指的是使用悲观锁。通常来讲在数据库上的悲观锁需要数据库本身提供支持,即通过常用的select … for update操作来实现悲观锁。当数据库执行select for update时会获取被select中的数据行的行锁,因此其他并发执行的select for update如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。select for update获取的行锁会在当前事务结束时自动释放,因此必须在事务中使用。

这里需要注意的一点是不同的数据库对select for update的实现和支持都是有所区别的,例如oracle支持select for update no wait,表示如果拿不到锁立刻报错,而不是等待,mysql就没有no wait这个选项。另外mysql还有个问题是select for update语句执行中所有扫描过的行都会被锁上,这一点很容易造成问题。因此如果在mysql中用悲观锁务必要确定走了索引,而不是全表扫描。

乐观锁

乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。

乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。一般的做法是在需要锁的数据上增加一个版本号,或者时间戳,然后按照如下方式实现:

1. SELECT data AS old_data, version AS old_version FROM …;
2. 根据获取的数据进行业务操作,得到new_data和new_version
3. UPDATE SET data = new_data, version = new_version WHERE version = old_version
if (updated row > 0) {
    // 乐观锁获取成功,操作完成
} else {
    // 乐观锁获取失败,回滚并重试
}

在数据库内部update同一行的时候是不允许并发的,即数据库每次执行一条update语句时会获取被update行的写锁,直到这一行被成功更新后才释放。因此在业务操作进行前获取需要锁的数据的当前版本号,然后实际更新数据时再次对比版本号确认与之前获取的相同,并更新版本号,即可确认这之间没有发生并发的修改。如果更新失败即可认为老版本的数据已经被并发修改掉而不存在了,此时认为获取锁失败,需要回滚整个业务操作并可根据需要重试整个过程。

乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能

乐观锁还适用于一些比较特殊的场景,例如在业务操作过程中无法和数据库保持连接等悲观锁无法适用的地方

28. group by、order by、distinct

order by 一种是通过索引取得有序数据,不需要进行任何排序操作,即可将有序数据返回客户端

另一种是通过mysql的排序算法,将存储引擎中数据排序,返回客户端。

group by 实际上也是同样的操作,与order by 相比group by 主要是多了排序之后的分组操作

  1. 使用松散的索引扫描group by

在没有where语句必须经过全索引扫描的时候,松散索引扫描需要读取的键值数量与分组的组数量一样多,也就是说比实际存在的键值数目要少很多。而在WHERE子句包含范围判断式或者等值表达式的时候,松散索引扫描查找满足范围条件的每个组的第1个关键字,并且再次读取尽可能最少数量的关键字

  1. 使用紧凑的索引扫描group by

当GROUP BY条件字段并不连续或者不是索引前缀部分的时候,MySQL Query Optimizer无法使用松散索引扫描,设置无法直接通过索引完成GROUP BY操作,因为缺失的索引键信息无法得到。但是,如果Query语句中存在一个常量值来引用缺失的索引键,则可以使用紧凑索引扫描完成GROUP BY操作,因为常量填充了搜索关键字中的“差距”,可以形成完整的索引前缀。这些索引前缀可以用于索引查找。而如果需要排序GROUP BY结果,并且能够形成索引前缀的搜索关键字,MySQL还可以避免额外的排序操作,因为使用有顺序的索引的前缀进行搜索已经按顺序检索到了所有关键字。

  1. 使用临时表实现GROUP BY MySQL在进行GROUP BY操作的时候要想利用所有,必须满足GROUP BY的字段必须同时存放于同一个索引中,且该索引是一个有序索引(如Hash索引就不能满足要求)。而且,并不只是如此,是否能够利用索引来实现GROUP BY还与使用的聚合函数也有关系。

1.尽可能让MySQL可以利用索引来完成GROUP BY操作,当然最好是松散索引扫描的方式最佳。在系统允许的情况下,我们可以通过调整索引或者调整Query这两种方式来达到目的; 2.当无法使用索引完成GROUP BY的时候,由于要使用到临时表且需要filesort,所以我们必须要有足够的sort_buffer_size来供MySQL排序的时候使用,而且尽量不要进行大结果集的GROUP BY操作,因为如果超出系统设置的临时表大小的时候会出现将临时表数据copy到磁盘上面再进行操作,这时候的排序分组操作性能将是成数量级的下降;

DISTINCT实际上和GROUP BY的操作非常相似,只不过是在GROUP BY之后的每组中只取出一条记录而已。所以,DISTINCT的实现和GROUP BY的实现也基本差不多,没有太大的区别。同样可以通过松散索引扫描或者是紧凑索引扫描来实现,当然,在无法仅仅使用索引即能完成DISTINCT的时候,MySQL只能通过临时表来完成。但是,和GROUP BY有一点差别的是,DISTINCT并不需要进行排序。也就是说,在仅仅只是DISTINCT操作的Query如果无法仅仅利用索引完成操作的时候,MySQL会利用临时表来做一次数据的“缓存”,但是不会对临时表中的数据进行filesort操作。当然,如果我们在进行DISTINCT的时候还使用了GROUP BY并进行了分组,并使用了类似于MAX之类的聚合函数操作,就无法避免filesort了

29. 一条sql语句,数据库做了什么?

  1. 客户端发送一条查询给服务器;
  2. 服务器先会检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果。否则进入下一阶段;
  3. 服务器端进行SQL解析、预处理,再由优化器生成对应4的执行计划;
  4. MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询;
  5. 将结果返回给客户端。

一条sql 的更新过程

1、首先执行器会找引擎取id=1这条数据; 2、因为id是主键,所以使用树来找到一行数据。不过引擎先去内存中查找是否有这一页数据; 3、如果有则直接返回数据给执行器;如果没有就会去磁盘把数据读入到内存中,然后返回数据给执行器。 4、执行器就会执行C+10操作; 5、执行器生成新的一行数据; 6、再调用 InnoDB 引擎的写入接口,把数据更新到内存中; 7、InnoDB 引擎写入 redo log 日志,标记状态为 prepare,并且告诉执行器已经更新数据完成,可以随时提交事务; 8、执行器把此操作写入 bin log ,并且把 bin log 写入磁盘; 9、最后执行器调用引擎的提交事务接口,引擎把 redo log 的状态改 commit ,至此整个更新操作完成。