MYSQL 索引相关问题问答

Q:我看你简历上写到了熟悉MySQL数据库以及索引的相关知识,我们就从索引开始,索引有哪些数据结构?

A:B+  Hash

Q:为什么哈希表、完全平衡二叉树、B树、B+树都可以优化查询,为何Mysql独独喜欢B+树?

A:hash 可能出现哈希冲突,哈希支持快速精准的查询,但不支持范围查询,如果做成了索引那速度很慢的,要全部扫描

Q:问个题外话,那Hash表在哪些场景比较适合?

A:等值查询的场景 (K  V)像redis memcached 这些nosql中间件

Q:你说的是无序的Hash表,那有没有有序的数据结构?

A:有序数组,在等值查询和范围查询的时候都很优秀

Q:那它完全没有缺点么?

A:不是的 有序的适合静态数据 ,我们 新增 删除 修改 就会改变他的结构,比如你新增一个在他后面的数据都要后移,成本很高

Q:那照你这么说他根本就不优秀啊,特点也没地方放

A:可以用来做静态存储引擎,比如可以拿来做2019年的支付账单啊,历史的 不会变动的数据

Q:有点东西啊小伙子,那二叉树呢?

A:二叉树是有序的 ,支持范围查询 时间复杂度是O(logN),为了维持这个时间复杂度 更新的时间复杂度也得是O(logN) 那就要保证这棵树是完全平衡二叉树了

Q:怎么听你一说,平衡二叉树用来做索引还不错呢?

A:索引也不只是在内存里面存储的,还是要落盘持久化的,可以看到图中才这么一点数据,如果数据多了,树高会很高,查询的成本就会随着树高的增加而增加。

为了节约成本很多公司的磁盘还是采用的机械硬盘,这样一次千万级别的查询差不多就要10秒了,这谁顶得住啊?

Q:如果用B树呢?

A:此处自己去查看B树的结构  

可以发现同样的元素,B树的表示要比完全平衡二叉树要“矮”,原因在于B树中的一个节点可以存储多个元素。

B树其实就已经是一个不错的数据结构,用来做索引效果还是不错的

Q:那为啥没用B树,而用了B+树?

A:同样的元素 B+树要比B树胖,原因在于B+树的非叶子结点会冗余一份在叶子节点中,并且叶子结点中用指针互连

Q:那么B+树到底有什么优势呢?

A:看一下上面的数据结构,最开始的hash不支持范围查询,二叉树树高很高,只有B树和B+树有的一比

B树一个节点可以存储多个元素,相对于完全平衡二叉树的树高降低了,磁盘IO效率提高了

而B+树和B树相比 增加了叶子节点的冗余 提高范围查找的效率,提高的原因是因为有指针指向下一个叶子节点

小结:到这里可以总结出来,MySQL选用B+树 数据结构作为索引,可以提高查询磁盘时磁盘IO的效率,可以提高查询范围的效率,并且B+树里的元素也是有序的

Q:那么,一个B+树的节点中到底存多少个元素最合适你有了解过么?

或者你可以换个角度来思考B+树中一个节点到底多大合适?

 A:B+树中一个节点为一页或页的倍数最为合适

Q:为啥呢

A:因为如果一个节点的大小小于1页,那么读取这个节点的时候其实也会读出1页,造成资源的浪费。

如果一个节点的大小大于1页,比如1.2页,那么读取这个节点的时候会读出2页,也会造成资源的浪费。

所以为了不造成浪费,所以最后把一个节点的大小控制在1页、2页、3页、4页等倍数页大小最为合适

Q:你提到了页的概念,能跟我简单说一下么?

A:首先Mysql的基本存储结构是(记录都存在页里边):

各个数据页可以组成一个双向链表

而每个数据页中的记录又可以组成一个单向链表

- 每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录

以其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。

所以说,如果我们写 select * from user where username='丙丙'这样没有进行任何优化的sql语句,默认会这样做:

定位到记录所在的页

- 需要遍历双向链表,找到所在的页

从所在的页内中查找相应的记录

- 由于不是根据主键查询,只能遍历所在页的单链表了

很明显,在数据量很大的情况下这样查找会很慢!看起来跟回表有点点像

Q:哦?回表你聊一下。

A:

回表大概就是我们有个主键为ID的索引,和一个普通name字段的索引,我们在普通字段上搜索:

select * from table where name = '丙丙'

执行的流程是先查询到name索引上的“丙丙”,然后找到他的id是2,最后去主键索引,找到id为2对应的值。

回到主键索引树搜索的过程,就是回表。不过也有方法避免回表,那就是覆盖索引
Q:哦?那你再跟我聊一下覆盖索引呗?

A:这个其实比较好理解,刚才我们是 select * ,查询所有的,我们如果只查询ID那,其实在Name字段的索引上就已经有了,那就不需要回表了。

覆盖索引可以减少树的搜索次数,提升性能,他也是我们在实际开发过程中经常用来优化查询效率的手段。

很多联合索引的建立,就是为了支持覆盖索引,特定的业务能极大的提升效率。
Q:索引的最左匹配原则知道么?

最左匹配原则:

索引可以简单如一个列 (a),也可以复杂如多个列 (a,b,c,d),即联合索引。
如果是联合索引,那么key也由多个列组成,同时,索引只能用于查找key是否存在(相等),遇到范围查询 (>、<、between、like左匹配)等就不能进一步匹配了,后续退化为线性查找。
因此,列的排列顺序决定了可命中索引的列数。
例子:

如有索引 (a,b,c,d),查询条件 a=1 and b=2 and c>3 and d=4,则会在每个节点依次命中a、b、c,无法命中d。(c已经是范围查询了,d肯定是排不了序了)
总结
索引在数据库中是一个非常重要的知识点!

上面谈的其实就是索引最基本的东西,N叉树,跳表、LSM我都没讲,同时要创建出好的索引要顾及到很多的方面:

最左前缀匹配原则。这是非常重要、非常重要、非常重要(重要的事情说三遍)的原则,MySQL会一直向右匹配直到遇到范围查询 (>,<,BETWEEN,LIKE)就停止匹配。
尽量选择区分度高的列作为索引,区分度的公式是 COUNT(DISTINCT col)/COUNT(*)。表示字段不重复的比率,比率越大我们扫描的记录数就越少。
索引列不能参与计算,尽量保持列“干净”。比如, FROM_UNIXTIME(create_time)='2016-06-06' 就不能使用索引,原因很简单,B+树中存储的都是数据表中的字段值,但是进行检索时,需要把所有元素都应用函数才能比较,显然这样的代价太大。所以语句要写成 : create_time=UNIX_TIMESTAMP('2016-06-06')。
尽可能的扩展索引,不要新建立索引。比如表中已经有了a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
单个多列组合索引和多个单列索引的检索查询效果不同,因为在执行SQL时,MySQL只能使用一个索引,会从多个单列索引中选择一个限制最为严格的索引(经指正,在MySQL5.0以后的版本中,有“合并索引”的策略,翻看了《高性能MySQL 第三版》,书作者认为:还是应该建立起比较好的索引,而不应该依赖于“合并索引”这么一个策略)。
“合并索引”策略简单来讲,就是使用多个单列索引,然后将这些结果用“union或者and”来合并起来

文章来源与参考:优快云博主「敖 丙」的原创文章
原文链接:https://blog.youkuaiyun.com/qq_35190492/article/details/104346265

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值