MySQL面试题
MySQL的执行流程:连接器 --> 分析器 --> 优化器 --> 执行器 --> innodb存储引擎;
B+树作为索引结构原因:B+树的非叶子节点不存储完整的数据,只存储索引字段数据,叶子结点是两两互相连接的;
MySQL面试题
说一说MySQL的执行流程
连接器 --> 分析器 --> 优化器 --> 执行器 --> innodb存储引擎
查询数据到bufferpool --> 记录undolog日志 --> 更新bufferpool数据 --> redolog日志(prepare状态)--> binlog日志 --> redolog日志(commit状态)--> 刷盘(持久化)
mysql的执行流程可以分为两部分,一部分是Server层,一部分是存储引擎层。
Server层的流程是先到连接器验证访问权限,创建连接,然后到分析器里校验语法等关键字的正确性,再到优化器里主要选择合适的索引等操作,最后到执行器里调用存储引擎层。
存储引擎层,如果是innodb存储引擎,则是先将需要操作的数据所在数据页从idb磁盘文件里读取到BufferPool缓存池的数据页里。
再记录更新操作到undolog日志里后,再更新BufferPool缓存池里的数据,再将日志记录到redolog日志里,并标记为prepare状态,然后再将整个事务的操作记录binlog日志,并刷盘,之后再将redolog日志里的prepare状态修改为commit状态(此为两阶段提交)。
最后系统会以数据页为单位将BufferPool里更新的数据写入磁盘,进行持久化。
详细介绍可查看《 MySQL架构的理解分析 》
MySQL为什么选择B+树作为索引结构
B+树的非叶子节点不存储完整的数据,只存储索引字段数据,叶子结点是两两互相连接的。
B树的特点是从上到下,从左到右是有序的。
相比于普通的二叉树结构,B树的一个节点可以存储多个元素。
每个节点的元素信息都是一条完整的数据记录,这就会导致当一条数据占用空间较大时,一个数据页能存储的数据就更少,整个树的层数会更多。
B+树同样是上到下,左到右是有序的,但是B+树的非叶子节点不存储完整的数据,只存储索引字段数据,完整的数据全部存放在叶子节点。
所以B+树只需要3层(一个数据页16KB,大概存1170条索引字段数据),就可以存储近2千万数据,查询时进行的磁盘IO操作更少,效率自然就比B树高出一大截。
非叶子节点中一个元素的左侧指针指向比自己小的元素所在子节点,右侧指针指向大于等于自己的元素所在子节点。
B+树的叶子结点是两两互相连接的,在进行范围遍历时效率更高,可以快速的遍历所有后续节点。
说说数据库索引的设计原则
使用短索引,如果需要对一个长字符串字段列进行索引,应该只取其一定长度的前缀字符构成索引即可(一般情况下,前20个字符串可以过滤出大多数的数据结果),可以节省大量的索引空间,一个数据页存储的索引字段也就更多,磁盘IO的次数也就更少。
不能创建过多的索引,在修改表内容时,是需要额外维护这些索引结构的,过多的索引,会导致总体的维护时间更长,也就降低了数据表写操作的性能。
小基数字段不建议创建索引,例如性别,只有男女两种值,数据过滤的性能就不太好。除非是向逻辑删除字段,删除的数据占最大多数,只有一小部分是有效的数据,那这个字段就可以创建一个索引。
可以建唯一索引的,尽量建成唯一索引。
创建组合索引时,按照最左前缀原则,根据使用频率从左到右排列创建,因为MySQL在使用索引时,是从联合索引的最左列开始向右匹配的。使用联合索引还有个好处,当查询不符合最左前缀原则时,MySQL8的高版本还支持跳跃扫描,当第一个字段的基数不大时,会跳过第一个索引字段的比对,直接比对第二个字段,因为组合索引在第一个字段相同的情况下,第二个字段是有序的,这么扫描起来相对于全表扫描,效率也是相对较高的。
where与order by冲突时,优先满足where去走索引。
代码先行,索引后上(等主体业务功能开发完毕之后再把涉及到的sql拿出来分析之后再设计创建对应的索引,这样创建的索引命中会更精准)
可以持续关注MySQL的slow.log慢sql查询日志,针对这些慢sql查询做特定的索引优化
什么是聚集索引和非聚集索引
存完整数据的就是聚集索引,不存完整数据,叶子节点是主键Id的是非聚集索引。
聚集索引也叫聚簇索引,在innodb存储引擎中,主键Id索引就是聚集索引。
他的叶子节点上面包含了数据表一整条完整的数据信息,这就叫聚集索引。
非聚集索引可理解为就是普通的非主键索引,其叶子节点上面不会存储一条完整的数据。
其叶子节点存储的是主键Id,所以我们在使用非聚簇索引时,如果查询完整数据,就需要进行一次回表操作,就是根据叶子节点上存储主键Id,去主键索引里进行回表查询完整的数据。
如果没有主键索引,则会选择第一个非空的索引作为聚集索引,如果没有主键索引,也没有其他的索引,则mysql会创建一个隐藏的rowid作为聚集索引。
为什么数据库主键不建议使用UUID
第一,因为uuid是无序的,在新增一条数据时,uuid因为无序,所以是插入在索引树中间部位的,这就导致这条数据后面的所有数据都需要进行重排,去保持索引的有序,这就导致插入的效率很低。
第二,uuid的长度为36个字节,相比于自增、雪花id等的8个字节相对较长,占用空间更多,也就导致索引数据结构里的一个索引页存储索引字段更少,导致了查询需要进行更多次数的io,效率相对自增和雪花id这类有序递增的id要低。
说一说你对explain的使用理解
type等级尽量达到range级别以上,然后rows尽可能的小,filtered列尽可能的大
在MySQL数据库里explain可以用来查看SQL语句的执行计划,然后可以根据其参数进行优化SQL。
核心重要的是id确定执行顺序(id大的先执行,相同的则排上面的先执行)、type执行性能等级、key预计会走的索引、key_len组合索引命中的字段数量、rows预估扫描数据行、extra会有一些参数,可以根据这些参数提示进行SQL优化例如using filesort,则标识排序用的是文件排序,可以给排序字段添加索引。
Using where表明存储引擎返回了数据之后,在Server层还进行过滤,如果rows的值很大,则说明索引的过滤效果不好,需要进行索引调整。
其中type等级尽量达到range级别以上,然后rows尽可能的小,filtered列尽可能的大(最大100,表明扫描的数据都是可用的)。
mysql索引有几种数据结构类型
我所了解到的mysql索引有BTree和Hash。
Hash数据结构类型的索引只适合进行唯一单字段精确匹配查询,所以基本上很少用它。
BTree数据结构类型是B+树数据结构的一个变种,聚簇索引仅叶子节点存储一行数据的完整信息,一个索引页就能存储更多索引数据,减少比对IO次数,并且,叶子节点之间有互相连接,适合进行范围查询。其3层树就可以存储上千万的数据,性能较好,基本上适合当下业务所有场景。
mysql的索引数据结构基本上追求的就是尽量少的IO,所以就二叉树就是比较好的选择,可以直接过滤掉一半的数据,但是极端情况下例如有序的数据,这个索引构成的二叉树就基本上是一个链表,相当于将所有数据遍历了一遍,这样肯定是不符合要求的。
红黑树(二叉平衡树)可以解决这个方法,插入数据后会进行自平衡调整,就杜绝了形成链表的情况,但这依旧有个问题,当数据量大了之后,这个红黑树的高度是不可控的,查询一个数据可能也要进行IO几十次。
于是就有了B树,一个节点上不再是一个数据,而是有序的一批数据,这样就大幅度减少了树的高度,减少了几十倍的IO次数。不过B树依旧存在一个问题,B树的所有节点都会存储完整的业务数据。这就导致一个节点存储的数据条数有限,依旧满足不了mysql的高性能查询。
最后就有了B+树,与B树不同,B+树的非叶子节点不存储完整的业务数据,只存储索引字段数据,只有叶子节点上才有完成业务数据。这就让一个节点的索引页可以存储的索引字段数据量大幅提升,减少了整个索引树的节点和控制了高度。
所以,从二叉树到B+树的整个演进史,其实就是数据库系统为了在减少磁盘IO次数和支持高效范围查询这两个核心目标上,所做的持续优化的历史。
大型项目中,mysql的主键需要全局唯一怎么办
有如下几个选项UUID、雪花Id、Redis自增、美团的Leaf组件。
UUID虽然高版本v7、v9是有序的,但是性能不如雪花id高,并且需要处理依赖兼容问题,可能会受到其他第三方依赖的影响。
比较推荐雪花Id,生成性能高,单机可以达到400~500万的QPS,且是有序的,但是存在个服务器时间回拨的问题,当发现服务器时间回拨后,需要等待,额外注重系统时间的统一性。
Redis自增,使用redis的incr命令进行自增1,因为redis的单线程操作机制,天然的就是不存在并发情况,性能高,但是需要保持redis的高可用,需要使用集群或哨兵,不然就会导致整个系统无法生成id。如果系统里已经有了redis集群,可以考虑,否则推荐直接使用雪花id,使用简单,不依赖第三方。
美团的Leaf,是美团研发团队开发的,有号段模式(一次从数据库获取一个号段的id),和雪花模式,性能高,如果是大系统,可以考虑。
为什么雪花算法比UUID好,好在哪里
Mysql索引什么时候会失效
说一下对MySQL的事务的理解,他有几种隔离级别
mysql有未提交读、读已提交、可重复读、串行读,默认的设置是可重复读。
未提交读是当前事务可以读取到其他事务更新还没有提交的数据,也就是脏读,一般不会使用该隔离级别。
读已提交,是所有事务,都只能读取到已经被commit提交的数据,还没有提交的数据对数据操作事务以外的其他事务是不可见的,很多公司会调整隔离别为读以提交。会提高性能,减少复杂的间隙锁等锁管理,以及减少MVCC日志版本链的日志版本数。
可重复读,
分布式事务你是怎么实现的
- 每种索引数据结构类型有什么区别?
- B树或者B+树的工作原理
- 既然你刚刚说了B+树性能高,那为什么不把所有树都换成B+树,还要红黑树二叉树干嘛?
- Mysql索引什么时候会失效
- 大型项目中,mysql的主键需要全局唯一怎么办
- 你刚刚说到了雪花算法,实现原理你知道吗?
- 为什么雪花算法比UUID好,好在哪里?
- MySQL的事务你说一下对他的理解,他有几种隔离级别
- 分布式事务你是怎么实现的
- 脏读和幻读的区别
