查询优化

全值匹配

SELECT * FROM `demo` WHERE a = 1;

在这种类型的查询语句中,如何命中索引非常简单直白,只要查询条件对应的列包含了索引列,就可以使用索引进行查询,所以上面三个查询语句都可以使用索引。而如果查询条件不包含索引列,或者没有命中,就不会使用索引而是通过全表扫描。

注:唯一索引与普通索引的性能对比,由于唯一索引全表只包含一行符合条件的记录,所以定位到索引值后可以立即返回查询结果,而普通索引需要将满足条件的查询结果都返回,这里可能包含多条记录,不过既然是索引,那就都已经做好了排序,因此符合条件的多条记录是扎堆在一起的,只需要通过简单的位移操作即可返回所有满足条件的结果集,除非满足条件的记录特别多,否则,两者的性能差异微乎其微,但是唯一索引在插入或者更新数据时需要额外的开销,所以一般情况下将索引设置为普通索引即可,然后把去重逻辑放到业务代码层实现

联合索引的优势和最左前缀原则

创建联合索引例如(b,c)的好处在于,首先可以减少索引b+tree的数量,减少维护成本,其次如果在已知 b 列值的情况下,再去过滤 c 列值就可以应用二分查找进行非常高效的查询了,并且如果查询字段只包含主键和索引字段的情况下,也可以避免回表操作

最后,对于联合索引的使用,查询条件不区分索引列的先后顺序,比如如下这条 SQL 语句,也会命中联合索引,这是因为在 InnoDB 存储引擎之上的 MySQL Server 层,有一个优化器对 SQL 语句进行优化,从而生成最优的执行计划。

字符串模糊查询

除了联合索引之外,最左前缀原则在数据库的字符串查询场景中也有体现,比如在模糊查询中,只有形如 WHERE d LIKE xxx% 这样的查询条件才能应用设置在 d 列上的索引。

其实细想一下背后的原因,字符串列值的排序和联合索引很像:从第一个字符开始比较,如果第一个字符相同,再看第二个,依次类推。。。把这里的字符对应联合索引中的列值,可不就是一个模子刻出来的嘛!

也就是说,对于应用了索引的字符串列而言,前 N 个字符都是排序好的,这也是为什么通过 'xxx%' 可以使用索引,而 %xxx 不行的原因,就好比 WHERE b = ? 可以使用索引,而 WHERE c = ? 不行。当然,如果你的业务逻辑需要匹配的是 %xxx 后缀,比如域名、邮箱字段,可以将字符串逆序存放到数据库,这样,就可以使用 xxx% 使用索引进行匹配了。

字符串前缀索引

此外,我们可以为字符串列设置指定长度的前缀索引,以便缩短索引长度,提高查询效率,降低空间成本:

CREATE TABLE `demo` (

  `a` int(10) unsigned NOT NULL,

  `b` int(10) unsigned NOT NULL,

  `c` int(10) unsigned NOT NULL,

  `d` varchar(32) NOT NULL,

  PRIMARY KEY (`a`),

  KEY `b_c` (`b`, `c`),

  KEY `d` (d(6))

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这里我们通过 d(6) 为 d 列的前 6 个字符创建索引,这样一来,初始化数据后,在 d 索引树的叶子节点中,索引值就是 d 列值的前 6 个字符,而不是完整的 d 列值。

至于这个前缀长度如何设置,可以先通过如下语句查询该字段有多少条相同记录:

SELECT COUNT(DISTINCT `d`) AS len FROM `demo`;

如果太多字段值相同不建议使用前缀索引,接下来可以进一步通过如下 SQL 语句获取该字段指定长度前缀有多少条相同记录:

SELECT COUNT(DISTINCT LEFT(`d`,4)) AS len4 FROM `demo`;

SELECT COUNT(DISTINCT LEFT(`d`,5)) AS len5 FROM `demo`;

SELECT COUNT(DISTINCT LEFT(`d`,6)) AS len6 FROM `demo`;

SELECT COUNT(DISTINCT LEFT(`d`,7)) AS len7 FROM `demo`;

SELECT COUNT(DISTINCT LEFT(`d`,8)) AS len8 FROM `demo`;


通过 LIMIT 优化未命中索引的查询

如果 where 条件中的字段可以命中索引,使用 LIMIT 限定可以非常高效地从已经排好序的 B+ 树索引数据返回满足 where 条件之后的限定条数数据,即便是 where 条件中的字段没有命中索引,通过 LIMIT 也可以提升性能。

关于 LIMIT 分页时偏移量过大引起的性能问题优化

不过,即便是 where 语句命中了索引,如果数据量很大的话,LIMIT 在分页查询中有一个非常常见的性能问题,就是随着分页偏移量越来越大,分页查询的性能也越来越低


因为偏移量是相对于 a = 1001 这个匹配到的第一个索引值来的,如果偏移量很大,就又相当于进行全表扫描了,所以这也是为什么你会看到很多大型网站只会提供前面几十页的真实分页,后面的分页查询都是不可用的。

对于这种分页查询,一个优化方式是改变 where 语句中的偏移量起始值,比如对于上面这个 查询,可以这样优化


这样一来,我们就又可以通过 B+ 索引树快速定位到 501001 这个索引值位置,然后在其基础上往后偏移 10 条记录,进行高效的查询了!

排序查询

然后我们来看看排序,排序操作通常是在数据库查询返回的结果集上进行的,如果结果集不大,可以直接在内存中通过快排、归并等排序算法完成,如果数据集很大,内存不能完全加载,还需要借助磁盘空间完成排序操作(这种排序被称作文件排序,显然,涉及到磁盘 IO 的操作性能肯定是不行的),最后把排序结果返回给调用的客户端。

当然,如果 where 条件语句命中了索引,由于 B+ 树索引数据本身已经做好了排序,则可以高效返回排序结果,这里的排序比较规则和索引排序一样,依然遵循设置的字符集和字符集比较规则

对于使用联合索引的字段而言,如果要设定多个排序字段,需要遵循索引列的顺序设置排序字段顺序,这样,才可以使用索引的排序,不过,对于结果集很小的查询,排序本身的开销可以忽略。

不能使用索引排序的几种排序查询

排序字段未出现在 where 语句中

当然如果排序字段没有出现在 where 条件语句中,或者压根没有 where 条件,是无法使用索引已有的排序的:

SELECT * FROM demo ORDER BY a DESC;

(a如果不是主键,将不会用到索引)

联合索引字段混合使用升序和降序

如果联合索引混合使用了升序和降序,则不能直接使用索引排序:

SELECT * FROM demo WHERE a BETWEEN 100000 AND 100010 ORDER BY a, b ASC;

排序包含函数调用

最后,对于使用函数的表达式,也通通不能使用索引排序:

SELECT * FROM demo WHERE d LIKE 'xxx%' ORDER BY upper(`d`) DESC;

这一点,不仅限于排序,如果是 where 语句中调用了函数,则也不能使用索引:

SELECT * FROM demo WHERE lower(`d`) = 'c51ce410c124a10e0db5e4b97fc2af39';

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

推荐阅读更多精彩内容