07 | 数据库索引:索引并不是万能药
07 | 数据库索引:索引并不是万能药
讲述:王少泽
时长25:25大小23.28M
InnoDB 是如何存储数据的?
聚簇索引和二级索引
考虑额外创建二级索引的代价
不是所有针对索引列的查询都能用上索引
数据库基于成本决定是否走索引
重点回顾
思考与讨论
赞 25
提建议
精选留言(40)
- Darren置顶2020-03-24第一个问题: 覆盖索引的cost是1.21而回表的是2.21 覆盖索引: analyzing_range_alternatives": { "range_scan_alternatives": [ { "index": "name_score", "ranges": [ "name1 <= name <= name1" ] /* ranges */, "index_dives_for_eq_ranges": true, "rowid_ordered": false, "using_mrr": false, "index_only": true, "rows": 1, "cost": 1.21, "chosen": true } ] 回表: "range_scan_alternatives": [ { "index": "name_score", "ranges": [ "name1 <= name <= name1" ] /* ranges */, "index_dives_for_eq_ranges": true, "rowid_ordered": false, "using_mrr": false, "index_only": false, "rows": 1, "cost": 2.21, "chosen": true } ] 第二个问题: SQL中带order by且执行计划中Extra 这个字段中有"Using index"或者"Using index condition"表示用到索引,并且不用专门排序,因为索引本身就是有序的; 如果Extra有“Using filesort”表示的就是需要排序; 排序时:MySQL 会给每个线程分配一块内存用于排序,称为 sort_buffer。sort_buffer_size(sort_buffer)的大小。如果要排序的数据量小于 sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。 上述排序中,只对原表的数据读了一遍,剩下的操作都是在 sort_buffer 和临时文件中执行的。但这个算法有一个问题,就是如果查询要返回的字段很多的话,那么 sort_buffer 里面要放的字段数太多,这样内存里能够同时放下的行数很少,要分成很多个临时文件,排序的性能会很差。所以如果单行很大,这个方法效率不够好。max_length_for_sort_data,是 MySQL 中专门控制用于排序的行数据的长度的一个参数。它的意思是,如果单行的长度超过这个值,MySQL 就认为单行太大,要换一个算法。称为 rowid 排序; rowid排序简单的描述就是先取出ID和排序字段进行排序,排序结束后,用ID回表去查询select中出现的其他字段,多了一次回表操作, 对于 InnoDB 表来说,rowid 排序会要求回表多造成磁盘读,因此不会被优先选择。展开
作者回复: 👍🏻
共 7 条评论84 - Darren2020-03-24另外分享下之前在公司分享的MySQL相关的PPT,主要从MySQL整体架构,引擎、索引、锁和MVCC这及部分分享的,感兴趣的同学可以看看,然后大家交流交流。 https://github.com/y645194203/geektime-java-100/blob/master/MySQL-%E5%88%86%E4%BA%AB%E4%BA%A4%E6%B5%81%E7%9A%84%E5%89%AF%E6%9C%AC.pptx展开
作者回复: 感谢分享
共 3 条评论47 - 袁帅2020-03-25老师的文章真的是太用心了,讲的太好了,赞👍
作者回复: 觉得好可以多分享
13 - 👽2020-03-24过早的优化,是万恶之源。不需要提前考虑建立索引。等性能出现问题,真正的需求降临的时候再考虑优化。 建立索引之前,需要考虑索引带来的副作用:维护成本,空间成本,回表成本。 更重要的是还要考虑,你的查询是否能用到索引。如果花费大量成本建立的索引,最后还用不上。那就赔了夫人又折兵了。 索引又牵扯到了很多注意事项,例如:尽量使用前缀匹配,而避免使用后缀匹配。因为后缀匹配会使得索引失效,走全表匹配。展开
作者回复: 总结的不错
13 - 马以2020-06-02老师这里讲的查询优化器选择流程详细一些,可否加个餐,说一说普通索引和唯一索引在什么业务场景下使用?
作者回复: 可以进一步阅读MySQL实战45讲专栏来了解相关内容,比如: 09 | 普通索引和唯一索引,应该怎么选择? 10 | MySQL为什么有时候会选错索引?
共 3 条评论7 - 汝林外史2020-03-24如果没有主键,就选择第一个不包含 NULL 值的唯一列。 没有主键的话,mysql不是会为每行数据默认生成一个rowid字段作为主键吗?? 排序索引失效应该也是对索引做了一些函数操作。 研读过mysql的专栏,所以这一节感觉的知识感觉都了解,不过这种感觉还是很爽的,期待老师更多的干货。展开
作者回复: 聚簇索引的规则是: When you define a PRIMARY KEY on your table, InnoDB uses it as the clustered index. Define a primary key for each table that you create. If there is no logical unique and non-null column or set of columns, add a new auto-increment column, whose values are filled in automatically. If you do not define a PRIMARY KEY for your table, MySQL locates the first UNIQUE index where all the key columns are NOT NULL and InnoDB uses it as the clustered index. If the table has no PRIMARY KEY or suitable UNIQUE index, InnoDB internally generates a hidden clustered index named GEN_CLUST_INDEX on a synthetic column containing row ID values. The rows are ordered by the ID that InnoDB assigns to the rows in such a table. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order. 因为这些太细节的东西和坑点没有太多关系,为了节省篇幅就不展开说了。 是的,当你读一些文章的时候发现99%的知识点自己都知道,完全是复习一遍的感觉,这种契合的感觉很舒服
5 - pedro2020-03-24第二个问题,我使用 EXPLAIN SELECT create_time from person ORDER BY create_time LIMIT 10; 语句,分析该 SQL,结果果然是 type = index,Extra = using index,证明排序走了索引并且是索引覆盖没有回表;我本以为加上函数就可以使索引失效,于是分别使用了 ORDER BY YEAR(create_time) 和 ORDER BY substring(create_time,1,3),发现 type 还是 index,我的数据库版本是8.0,请问老师,有什么方式可以让索引失效呢?
作者回复: select *,你select仅仅是索引列,那么走索引更划算了
4 - 请叫我和尚2020-04-13这一篇文章,我感觉哪里都是重点,哈哈哈哈4
- 海战2020-03-25利用jpa 完成查询,是不是都会发生回表呀,不知道会不会产生影响
作者回复: 文中回表是指二级索引查询后回聚簇索引,这个和jpa没什么关系
共 2 条评论3 - 三分之二2021-01-30再使用二分搜索出 #3 槽和 #6 槽的中间位是 (3+6)/2=4.5 取整 4,#4 槽对应的记录是 16>15,所以记录一定在 #4 槽中; 【所以记录一定在 #4 槽中;】这里应该是#3槽。共 1 条评论2
- Geek_3b10962020-03-26讲解非常细致谢谢老师2
- 小胡子2020-08-28尽量不要在 SQL 语句中 SELECT *,而是 SELECT 必要的字段,实际使用中能做到实在太少了共 1 条评论1
- LiG❄️2020-04-27老师好优秀,讲的非常透彻~自己还要要实操,加深理解!
作者回复: :)
1 - hellojd2020-03-28有时候一个表索引太多,也会带来成本
作者回复: 是的
1 - pedro2020-03-24第一个问题,我仔细对比了 optimizer trace 下的索引覆盖和回表,发现索引覆盖的如下项: "range_scan_alternatives": [ { "index": "name_score", "ranges": [ "name1 <= name <= name1" ], "index_dives_for_eq_ranges": true, "rowid_ordered": false, "using_mrr": false, "index_only": true, "rows": 1, "cost": 1.1056, "chosen": true } 我猜测是 index_only =true 告诉我们这是索引覆盖而不需回表,可以索引覆盖的 cost_for_plan 为 1.1056 而回表的 cost_for_plan 为 0.61,这是不是证明索引覆盖所付出的损耗还要高于回表了?展开
作者回复: 我这边做实验的结果在源码的coveringindex.jpg中可以看一下
1 - 夏天#风之友2022-04-12老师,你好,看到文章很受启发。我这里有个问题请教一下,项目经常遇到多条件查询,例如,记录表数量级大,想着添加多字段的二级索引(比如 index_test(a,b,c))来提升效率,但由于查询条件字段是动态拼接,数量不定(比如:abc,ac,bc,a,b,c)。这样会导致无法每次查询都会命中索引。 像这样的情况,老师有什么更好的查询方案?感谢
作者回复: 数量大,字段多,这种搜索就用ES好了
- SharpBB2022-03-13秀啊 特别是不走索引的情况 原来mysql会自行判断成本 难怪dba是一个单独的职位 数据库方面的学问很深啊
- Chasel👻2021-08-26老师,根据阿里的开发文档,业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。这个字段组合如果特别长的话也需要建立唯一索引么?实际开发中是怎么考量的
- 防腐基2021-07-16二分法什么的一头雾水,下面也就看不下去了……
- batman2021-07-02主键聚族索引是存在磁盘还是内存,如果是磁盘,每次都查磁盘岂不是很慢,如果是内存,怎么能放下那么数据
作者回复: 磁盘+内存