聊聊Mysql索引查询技巧

mysql的索引是日常开发中用到比较多的概念,对于千万级的表来说,能充分的利用索引,便能充分的提高查询效率。之前都是用到什么就查什么,没有一个总体汇总的体系。索引使用的经验真的是和自己实际的使用关系很大,以InnoDB为例简单的总结一下:

1.索引优点

  1. 索引大大减小了服务器需要扫描的数据量。
  2. 索引可以帮助服务器避免排序和临时表。
  3. 索引可以将随机I/O变为顺序I/O。

2.主键查询走索引
对于索引来说,最基本的就是主键索引了,在建表的时候都会指定主键。主键也默认的会建立索引,我们一般使用的索引都是Btree索引,大概结构如下:

Paste_Image.png

MyISAM和InnoDB索引结构有很大差异,这里以InnoDB为例,InnoDB的叶节点存储的是数据的行,而除了主键之外的列索引存储的是主键key,也就是说在查询的时候需要二次查询,先通过列索引找到主键,再通过主键索引找到row。而MyISAM的主键索引和列索引一致,只不过主键不可以重复,但是列可以。
上面的索引结构图告诉我们,在查询时,能使用主键查询尽量不要用列查询,因为会带来二次查询。
2.单个索引还是组合索引
上面我们提到一个查询过程,其实对于普通列的查询会转化为两次查询,那么我们应该尽量能在列的查询中过滤掉尽可能多的数据。
单个列索引查询也有个限制,如果查询的列不是独立的,那么Mysql不会使用索引。独立的列指查询中的列不能使用表达式,也不是函数的参数。
select uname from user where rootid + 1 = 10;
上面的sql是不会走索引的,因为需要对所有的rootid列进行计算,所以要全表扫描,如果改成下面的语句则可以避免
select uname from user where rootid = 9;
所以索引列是不可以做运算的。

在老版本的Mysql中是只能用一个索引进行查询的,在5.0之后的Mysql,引入了“索引合并”的概念。虽然能利用多个索引查询,但是索引合并不是很合理的:

  1. 在做and操作的时候单个索引(组合)的性能是远好于多个独立索引的。而且在and时,通常只能利用多个索引中的一个进行查询。
  2. 对于or操作,对于多个列索引的情况下,需要耗费大量的CPU和内存在数据的缓存、合并和排序上。
  3. 优化器不会把所有这些消耗计算到“查询成本”中,导致查询成本被远远低估。

所以说,其实“索引合并”只是一个优化方案,多于多个索引的扫描后计算,明显性能上是要差于单索引的查找。

下面看一个单列查询的例子:


Paste_Image.png

这里我的mobile和uname字段都是索引字段,但是从执行计划中可以看到只是走了uname的索引。也就是说mobile的索引是没有用到的,那么用什么方法让查询也利用到mobile索引呢?
针对我们经常查询的多列场景,我们可以建组合索引,组合索引在可以尽可能多的运用列的查询规则。说到组合索引那么必须说一下最左前缀原则:
最左前缀原则指的的是在sql where 子句中一些条件或表达式中出现的列的顺序要保持和多索引的一致或以多列索引顺序出现,只要 出现非顺序出现、断层都无法利用到多列索引。
假如:现在对于一个table建立了一个组合索引(uname,age,mobile),那么对于查询时where语句查询条件必须为(uname,age,mobile)或(uname,age)或(uname),此时才能利用组合索引。为什么呢?
因为我们知道mysql为btree索引,对于每一个点索引数据都会维护一个(uname,age,mobile)的复合数据结构,我们在Query的时候一定会以一个字段为第一个匹配,第一个字段匹配之后选取第二个字段进行匹配依次类推。那么对于(age,mobile)、(age)、(monile)这种情况没有第一个匹配项自然不会走索引,而(uname,mobile)也不会走索引。
下面例子的索引是(uname,mobile,email)的组合索引:
当使用(uname,mobile,email)时:


Paste_Image.png

可以看出查询全部走了索引。
当使用(uname,mobile)时:
Paste_Image.png

此时也走了索引。
但是如果是(uname,email)时:


Paste_Image.png

这时候只是走了uname的索引。
如果为(mobile,email)时:
Paste_Image.png

此时完全没有走索引。

4.索引的区分度
索引的区分度,主要是衡量索引值不相同的程度,区分度越大,越有利于索引的查询。
设想一下,对于sex列,列值只有male和female,那么也就是说列中绝大多数值都是重复的,那么用此索引进行row的查找其实意义并不大。所以这样的列建索引的意义并不大。
另一种场景,对于列值比较长的列,我们往往不能将整个列做索引,因为这样会导致索引过大,降低索引效率。我们需要取列值的前缀进行索引,那么索引前缀的大小选择就需要计算区分度。
索引的区分度计算主要计算是通过 不重复的索引值/数据表的总记录数。区分度越高,索引查询时会让mysql在查询时过滤掉更多的行。值越接近1,证明区分度越高。

5.组合索引的顺序和区分度:
上面说到了区分度越高的越容易用来做索引,因为区分度高的列可以很容易的过滤掉很多的数据。对于组合索引来说,在考虑索引的顺序的时候也是要考虑数据的分布,也就是区分度。对于多个列构成的组合索引,在查询过滤的时候也是和列的位置有关的,这也是最左前缀规则说的事情,也就是说如果在第一次能过滤掉大量的数据,那么后续的索引匹配就能减少很多消耗。所以在选择索引顺序的时候最好是要考虑到区分度的问题,将区分度比较高的列放在前面。

6.利用索引进行排序
Mysql可以通过两种方式达到排序的效果:

  1. 进行排序计算
  2. 按照索引顺序扫描

对于后者,扫描索引是很快的。但是如果索引不能覆盖查询所需的全部列,那么对于每一次查询都会回表查询一次行。这基本都是随机IO。这种情况下按照索引顺序读取数据反而会慢于全表扫描。
只有当索引的列顺序和Order By子句的顺序完全一致时,并且所有的列的排序方向都一样时,才能使用索引对子句进行排序。
也就是说索引的排序必须保证最左前缀规则,当然也有例外,就是在where子句中指定索引列为常量,同时保证where中条件和order by中条件满足最左前缀规则。首先看一下满足最左前缀规则的order by:

explain select uname from testuser order by uname,mobile;
Paste_Image.png

上面的表的索引是(uname, mobile, addtime),可以看到,查询走了组合索引,同时查询使用了覆盖索引。
除了上述的方式,还可以这么写:

explain select uname from testuser where uname = 'zhangsan' order by mobile,addtime;
Paste_Image.png

可以 看出来Extra中没有filesort,证明排序走了索引。
当排序条件顺序不一致时:

explain select uname from testuser where uname = 'zhangsan' order by mobile asc,addtime DESC;
Paste_Image.png

可以看到,Extra中出现了filesort,也就是说排序没有用到索引,所以排序顺序不一致的情况下是不能利用索引进行排序的。

上面都是针对组合索引的,单列索引对于索引排序意义不大,order by 只有在使用排序字段索引时才会有用。

针对排序这种场景,其实还可以在业务中实现排序,这样能大大的减轻数据库的压力,不至于因为一个查询而影响其他业务。

7.应该注意的几点

  1. 在使用索引查询的时候,需要保证索引类型和查询的数据类型一致,经常混用的是用int型查询varchar类型的数据或反过来,这样会导致索引失效。
  2. range查询要尽量放在后面,因为在range后面的查询不会走索引,这一点在设计索引的室友要注意
  3. Like查询不能前缀模糊匹配,也就是说不可以like ‘%123’。因为like的后缀模糊 like ‘123%’可以转化为range查询,但是前缀模糊不可以。
  4. 索引不是越多越好,索引十分大时不仅会影响查询效率,同时会为数据的插入造成很大的负担。
  5. 对于重复索引需要删除,规划好索引是高效率的前提。

以上是最近学习mysql索引的笔记,如果有什么不正确的地方,恳请指出,找错改错的过程很重要。勿忘初心,方得始终~

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

推荐阅读更多精彩内容