MySQL索引概述与读书笔记(持续更新)

提示:本文毫无可读性,完全是自己的读书笔记


叶子页的分裂

主键索引一次叶子页分裂将导致其余索引上大量的磁盘I/O;对主键索引列的更新操作,会引起索引行的异动,从而导致叶子页的分裂。    

索引的选择性(与Cardinality有关)

选择性=cardinality/总行数

选择性越是接近于1越好,换言之,cardinality越大越好

以常见的反例“性别”来进行距离,cardinality=2,选择性无限趋近于0,这无疑是非常糟糕的

最差情况下的选择性比平均选择性更重要,因为如果照顾了平均选择性而忽略最差选择性的话,有可能造成某一个SQL查询特别慢

选择性的好坏,与后面提到的索引片有很大的关系;选择性越好,索引片越窄

cardinality是一个估算值(忽略同一字段建2个索引的睿智操作,不是我干的!)

相同字段下,不同时段创建的2个单一索引,Cardinality值不同

怀疑是统计没有更新,手动执行更新

(t表1500万行数据,在空闲库执行下面的语句时间338s,不要在忙时去执行这个语句)

alter table t engine=innodb;

执行后的索引信息如下

可以看到值与之前统计的值不同,而且同一字段对应的2个索引的cardinality值仍然不同

此外,在show index这里展现的cardinality估算得并不准确,甚至可能出现非常大的偏差

比如这个例子中的monthly_settlement,看起来在这个索引中,这一列的选择性是最好的,station_id的选择性看起来就没这么好

然而实际情况并非如此

所以cardinality只能当做一般性的参考,如果要严格的计算选择性,还是需要用   “distinct 列名/总行数”     这种方式去计算,值越趋近于1,说明该列选择性越好


组合谓词的选择性(谓词:前面提到的3要素中的索引列)

简言之就是2个单列索引的效果可能远不如这2个字段的联合索引

优化器在评估访问路径成本时,必须先评估索引的选择性。(其实选择性的好坏也是三星索引的第一颗星)

等价的索引

where a=:a and b=:b

如果创建索引(a,b)和索引(b,a),这2个索引是等价的,因为where语句后面跟的2个都是等值条件,如果其中一个是范围条件,那么这2个索引就不等价了


回表采用的是随机读

三星索引

第一颗星:索引片的宽窄;

第二颗星:索引是否能够处理排序;(避免排序)

第三颗星:查询列是否在索引中

一般来说,第一颗星要比第二颗星重要,一个尽量窄的索引片比避免排序要重要;但这只是一般来说

下列例子说明了第一颗星的重要性

#sql语句

SELECT MAX(id)

FROM t

WHERE '2019-01-07 23:59:59' >= create_time

GROUP BY user_id;

索引1:idx_ct_uid(create_time,user_id)

索引2:idx_ct(create_time),idx_uid(user_id)

user_id是一个varchar(30)的列

全表行数为270W行,使用索引1的情况下,执行时间为6s,使用索引2的情况下,执行时间为1.5s

归结原因,条件列是一个范围查询,索引1能用到的其实只有create_time这部分,但是user_id列的加入,使得这个联合索引的索引片宽度大增,因此花了更多的时间去扫描。


宽索引

一个索引如果(至少)包含了第3颗星,那么这个索引就是一个宽索引

困难谓词(索引失效问题)

简单来说就是参照下面的口诀:

全值匹配我最爱,最左前缀要遵守

带头大哥不能死,中间兄弟不能断

索引列上少计算,范围之后全失效

like百分写最右,覆盖索引不写星

不等空值还有or,索引失效要少用

表连接的索引

关于嵌套循环

本地谓词和连接谓词

只用于访问一张表的谓词被称为本地谓词

定义了表和表之间的连接关系的谓词被称为连接谓词

抛开比较复杂的嵌套查询

只有本地谓词的那张表,将会成为外表,而另一张表自然成为了内表

外表也就是驱动表,要求是尽量的小

外表的小并不是指外表的总行数小,而是指经过外表的过滤因子之后取得的索引片比较小,这样会获得更好的性能

在嵌套查询中,内表产生大量随机读取是影响性能的一个关键因素,因此内表需要有一个比较好的宽索引,并且以连接谓词作为前导列(也就是连接谓词作为最左)

eg

select .... from A,B

where A.a=:a

and B.b=A.b;

在上面这个查询中,A.a是A表的本地谓词,所以A表很明显的就是外表,而B表就是内表,其中B.b是B表的连接谓词,为了减少内表的随机读,内表上需要有一个合适的以B.b为前导列的宽索引。

(由于这里的连接条件里只有B.b=A.b,如果B.b的选择性很差,那么这个内表的查询效率有可能很低,这种情况下,可以通过补全一些谓词去提高连接谓词的选择性(冗余字段),可以使内表尽可能快的查出结果,毕竟一般来说外表都是小表,内表相对较大,对性能的影响是比较大的。该段参考《数据库索引设计与优化》第8章的那个例子(CCTRY),该案例中有一个补全FF缺陷的技巧; 这个技巧在P178页中被描述为向下反范式,也就是内表加列;与之对应的还有向上反范式,也就是外表加列,不过需要注意所加冗余列的易维护性)

对于or的优化

可以改写成union+order by的形式

但是否能优化好,取决于一些条件是否完全匹配

有一些语句即使索引优化的很好,但union + order by仍然造成性能问题


--2019.7.25验证2年前的笔记

使用索引进行排序:(避免排序)

order by也需要满足索引最左前缀要求,才能用索引排序

比如索引为idx(b,c)

select xxx from table_a where a='x' order by b,c;

除此之外,索引列是一个常量的情况下,即使order by的部分不满足最左,也可以使用

比如索引为idx(a,b,c);索引列为常量的情况,说的就是where a='x'(如果是a>'x',或者是a in('x','y')则属于范围,不属于常量)

所以下列索引列和order by组成了a,b,c的顺序,满足了idx(a,b,c)的顺序

select xxx from table_a where a='x' order by b,c;

但是如果索引列为a>'x',这是一个范围查询,那么上面的语句无法使用idx(a,b,c)进行索引排序(下面的语句可以,因为order by满足了最左)

select xxx from table_a where a>'x' order by a,b;

关于最左前缀之前的一个理解错误的地方

idx(a,b,c)

select xxx from table_a where c='t' and b='y' and a='x'这样仍然会走索引,虽然顺序不一样,但是a,b,c都在where条件里面,并不需要把where写成a,b,c的顺序才行。

一些无法使用索引排序的例子;(索引仍然为idx(a,b,c))

1.逆序导致的方向不对

select xxx from table_a where a='x' order by b desc,c desc;


上面2张图对比可以看出desc字段没有走索引,但是从key_len来看2者没有变化,key_len记录的应该只是where后面走索引的字段,而对于排序部分是不记录的,至少5.7版本是这样

2.order by有其他列

select xxx from table_a where a='x' order by b,d;

3.中间列(b)断了

select xxx from table_a where a='x' order by c;

4.索引列有一个是范围查询

select xxx from table_a where a='x' and b in ('x','y') order by c;

5.延迟关联,本表会被当成第二张表,因此也无法使用


eg.排序走索引测试

涉及的字段,type,status,create_time,update_time(重要:下文以a,b,c,d代替)

idx_0对上述4个(a,b,c,d)字段建有联合索引

idx_1对前面2个(a,b)字段建有联合索引


语句1

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time;

where a=,b= order by c类型,加上查询字段的d,覆盖索引,排序走索引

语句2

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time desc;

where a=,b= order by c desc类型,虽然排序是倒序,但仍然走了索引,并没有filesort

语句3

select update_time from tra_trade where type='POS_ONLINE_PAY' and status!='SUCCESS' order by create_time;

where a=,b!= order by c 类型,出现b列出现了不等于,相当于(a,b,c)中的b断了,a肯定能走索引的,从key_len来看,b也走了,后面的c走不了,c出现了排序

语句4

select update_time from tra_trade where type='POS_ONLINE_PAY' and status!='SUCCESS' order by type,status,create_time;

where a=,b!= order by a,b,c 类型,全都走了索引

语句5

select update_time from tra_trade where type='POS_ONLINE_PAY' and status!='SUCCESS' order by type,create_time;

where a=,b!= order by a,c 类型,排序中的(a,b,c)断了,c走不了索引,出现了排序

#前面5个类型属于必须要记住的分类,后面的十几个语句算是更细节的探索

语句6

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by status,update_time;

where a=,b= order by b,d 类型,排序中的(b,c,d)断了,d肯定走不了索引会出现排序;这个语句的疑问在于b不满足最左,同时也不像前面的where a,b order by c那样满足连贯,b是否能走索引?

语句6变种

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by status;

将语句6中排序的d列(update_time)去掉后,语句变成了where a,b order by b类型,没有产生排序

语句7

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by status,create_time;

where a=,b= order by b,c 类型,语句7是前面语句6变种的延伸,与6的变种一样,没有产生排序

语句8

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by status,create_time desc;

where a=,b= order by b,c desc 类型,与7一样,没有产生排序


语句9

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time desc,status,type;

语句10

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time,status,type;

语句11

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time,type;

语句12

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time,status;

语句13

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time,type,update_time;

语句14

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time,status,update_time;

9和10都看作是where a=,b= order by c,b,a 类型,区别只是c是否有带desc;11是where a=,b= order by c,a 类型;12是where a=,b= order by c,b 类型;13是where a=,b= order by c,a,d 类型;11是where a=,b= order by c,b,d 类型;全都没有产生排序


语句15

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by update_time;

where a=,b= order by d 类型,(a,b,d断裂),d肯定走不了索引,产生排序

语句16

select update_time from tra_trade where type='POS_ONLINE_PAY' and status='SUCCESS' order by status,payee_charge_amount;

where a=,b= order by b,e 类型,e都不在索引里,肯定产生排序,此外排序列不在索引列,using index都没了,把覆盖索引都给破坏了


语句17

select * from tra_trade force index (idx_0) where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time;

select * from where a=,b= order by c类型

语句18

select * from tra_trade force index (idx_0) where type='POS_ONLINE_PAY' and status='SUCCESS' order by create_time desc;

select * from where a=,b= order by c desc类型

17和18的排序一个正序一个倒序,但查询列从索引的d字段变成了*,首先排序肯定是没有产生的,但有意思的是执行计划里面的extra字段变了;正序走了icp,倒序就走了个using where


18个语句的测试结论:

1.order by中的倒序,不会产生排序(filesort);如果你的执行计划里面产生了filesort,起码不能简单归结于是排序中有desc

2.如果where部分是常量,且查询部分没有出现中间断裂的情况,那么order by可以不用遵循最左原则

3.如果where部分有范围(不等于相当于范围),那么order by必须遵循最左原则

4.where部分与order by部分必须避免出现中间断裂的情况,只要出现中间断裂,必然产生filesort;只要不出现中间断裂,无论order by部分字段的先后顺序如何都不会产生filesort

5.排序中的desc并不会产生filesort,但desc是否会影响数据的过滤,不得而知,至少从17和18这2句的执行计划可以发现extra有明显的不同


#第4点字面很难看明白,特别解释一下

首先说明一下怎样算断裂:断裂点通常有2个地方,一个是where和order by接壤的地方,比如where后面是ab,order by是d,中间的c没有,就断掉了;另一个地方就是order by自身,比如where是ab,order by是abd,也是漏掉了c;但如果where部分是ab,order by部分是ac,看起来order by部分好像断掉了,实际上把where和order by连起来看,abc都已经存在了,所以就不存在中间断掉的情况。 此外,前面都是在where条件为常量的情况下,如果where部分存在范围,那么order by必须满足最左,并且一旦order by中存在断裂则视为断裂

结论第4点前半句中标粗的“避免中间断裂”的意思是where部分是ab,索引部分可以是c,或者cd,或者abc,或者是ac,或者bc;总之确保where和order by连起来是一个ab,c或者ab,cd或者ab,abc或者ab,ac或者ab,bc之类的模式,但绝对不能出现ab,d这种模式(也就是说where或者order by中只有a,b,d但),一旦出现断裂,则必然出现filesort

第4点后半句中的“部分字段的先后顺序”的意思是无论是order by abc还是cba还是cab都无关紧要,都不会出现filesort

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 9,355评论 0 23
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,279评论 0 10
  • 什么是DPI? DPI(Dots Per Inch):每英寸点数,表示指屏幕密度。是测量空间点密度的单位。 什么是...
    petite_chen阅读 778评论 0 1
  • 文/罗总 对于购物车里的商品,总会感觉这也很好,那也不错,其他的也还行,于是我们全选,哚手这个词听起来痛,实际感觉...
    Oxford草地阅读 271评论 0 1
  • 14 年会上成功的避开了所有大奖,然后放假了,终于熬完了一个多月996的加班,开心~ 然后回家窝在沙发烤着脚就根本...
    浅夏2阅读 164评论 0 1