这是数据库索引相关内容的第五篇
发现不合适的索引
触发我们考虑考虑索引是否合适的契机有两种
一种是:生产环境中出现查询慢,我们急于解决现实遇到的问题;
一种是:在设计实现阶段,我们希望提前发现设计不合理的索引,以免后续发布以后才出现性能问题;
对于 情况,我们可以通过提问来反思如何改进索引。
1. 是否所有where子句中的所有列都在索引中了?
如果没有,则添加到索引中,将索引变成半宽索引
2. 是否将所有涉及的列都加入到索引中了?
如果变成半宽索引后,还是没有解决到性能问题,那么下一个选择就是将查询中所有涉及的列都加入到索引中,形成宽索引;这样,优化器的访问只需访问索引,避免了表访问。
3. 你需要最佳索引
如果上述两种方式,仍未解决性能问题,则要参考《什么是最好的索引》一文,好好考虑一下索引的设计了
例如:SELECT A FROM XXX WHERE B = 1 AND C = 2;而索引是(A, B, C) 按照《索引》中的优化器逻辑,其索引片是空,即其查询将进行全索引扫描;如果索引超过10w条记录,那么查询将会很慢
但是按照上述的检查方式,第一条和第二条其都是满足的,但是该索引在一定的数量级下依然会导致查询效率慢,那么就要做第三步,对索引进行重新考虑了。
在正式运行的系统中,建议扩充索引列,而不是新增新的索引或者更换索引列的顺序,和将带来额外的负担。
对于 情况,我们可以通过系统的评估来查看索引是否合适。
1. 统计本地相应时间
直接先上结论:
本地响应时间(LRT) = 随机访问的数量(TR) * 10ms + 顺序访问数量(TS) * 0.01ms + 有效FETCH数量(F) * 0.1ms
什么是随机访问:就是一次磁盘IO的时间,约为10ms
什么是顺序访问:一页包含n行,每行的时间约为0.01ms;
再详细一点:
DBMS读取一个索引或一个表行的成本,即为一次访问;
DBMS扫描索引或表的一个片段,其中第一行的读取即为一次随机访问;
对于后续行的读取,每行都是一次顺序访问;
打个比方:
对于去超市买10个罐头:
随机访问就好比在超市找到罐头的货架的时间,就是10ms
顺序访问就好比已经找到罐头的货架了,只要一个个把罐头拿下来,每个罐头的时间就是0.01ms
好了,知道了随机访问和顺序访问,接下来我们知道如何确定随机访问和顺序访问的次数
索引访问次数
可以将索引当成一张表,其行数与其包含的表的行数相同,且按照索引键值排列
表访问次数
我们假设一次全表扫描将需要一次随机访问和N-1次顺序访问
2.举例:
主键索引:select CNAME, CNO, CDESC from table1 where CNO = 221
其中CNO为主键;
索引存储如下:
111,
112,
113
...
221,
222
那么优化器是如何检索的?
i. 根据CNO=221,进行一次随机访问,取到221这条索引
ii. 根据221这条索引指向的磁盘位置,通过一次随机访问,找到数据块,得到CNAME,CDESC
那么LRT是多少?
很好计算:
两次随机访问 + 1次FETCH = 2 * 10ms + 0.1ms 约等于 20ms
select CNO, CNAME, CDESC from table1 where CTYPE = 1 and CNAME = 'ZHANG' order by CDESC
假设索引是(CTYPE, CNAME, CDESC) 假设CTYPE =1 和CNAME='ZHANG'能从10w的索引中过滤出1000条
1000条索引如下:
1,'ZHANG', 1
1,'ZHANG', 2
....
1,'ZHANG', 1000
那么LRT是多少?
首先,一次随机访问索引的时间,定位到索引1000条的第一行
其次, 1000次顺序访问索引的时间
然后,因CNO不在索引中,所以还需要通过索引进行磁盘查找;
因为是聚簇索引,所以表的顺序和索引的顺序是一致的,访问表的时间和索引是一样的,即一次随机访问表的时间,和1000次顺序访问的时间(999不好计算,我们都约等于1000)
LRT= 1次索引随机访问 + 1000次索引顺序访问 + 1次表随机访问 + 1000次表顺序访问 + 1000次FETCH
= 10ms + 1000 * 0.01ms + 10ms + 1000 * 0.01ms + 1000 * 0.1ms
= 140ms
同2.2 ,如果同样是该查询语句,但是索引变成非聚簇索引会怎么样?
很显然,表的存储会发生变化,不再是跟索引的顺序一致,并且不是连续存储了;
所以,
LRT = 1次索引随机访问 + 1000次索引顺序访问 + 1000次表随机访问 + 1000次FETCH
= 10ms + 1000 * 0.01ms + 1000 * 10ms + 1000 * 0.1ms
= 10s(约等于)
你看,同样的1000条索引,查询速度和2.2相差这么多!
这个索引该如何优化呢,很显然,如果它不是聚簇索引,就要将CNO纳入到索引中来,避免表的随机访问;将所有的访问都回到索引内部
很显然,这就是《什么索引是好的索引》中介绍的所谓的好的索引,知识都是相辅相成的。
使用了该更改后的索引,其LRT会变成多少?
LRT = 10ms + 1000 * 0.01ms + 1000 * 0.1ms = 120ms
速度提升了100倍!
好了,本文重点介绍了如何在实际生产环境中和设计过程中发现问题及优化索引,只要掌握原则,了解思路,剩下的就是运用了。
其他相关章节
数据库索引相关文章之一:《B树,一点都不神秘》
数据库索引相关文章之二:《B树很简单,插入so easy》
数据库索引相关文章之三:《索引》
数据库索引相关文章之四:《什么索引算是好的索引》
数据库索引相关文章之五:《如何发现及替换不合适的索引》
数据库索引相关文章之六:《索引总结》