暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

技术分享 | MySQL 覆盖索引优化案例一则

182
作者:刘晨
网名 bisal ,具有十年以上的应用运维工作经验,目前主要从事数据库应用研发能力提升和技术管理相关的工作,Oracle ACE(Alumni),腾讯云TVP,拥有 Oracle OCM & OCP 、EXIN DevOps Master 、SCJP 等国际认证,国内首批 Oracle YEP 成员,OCMU 成员,《DevOps 最佳实践》中文译者之一,CSDN & ITPub 专家博主,公众号"bisal的个人杂货铺",长期坚持分享技术文章,多次在线上和线下分享技术主题。
本文来源:原创投稿
*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

最近有个MySQL的SQL性能问题,原理可能很基础,但考察的就是能不能将"显而易见"的知识应用到实践中。
经过脱敏的SQL如下所示,对test表中的c1列进行聚类,再通过SUM ... CASE WHEN...等函数进行统计,test表数据量500万,当前检索用时55秒,需求是将执行降到秒级,
SELECT c1,
       SUM(CASE WHEN c2=0 THEN 1 ELSE 0 END) as folders,
       SUM(CASE WHEN c2=1 THEN 1 ELSE 0 END) as files,
       SUM(c3)
FROM  test
GROUP BY c1;

复制

为了更好地说明,创建一张测试表,主键字段是id,除了c1、c2、c3字段外,还有其他字段,有很多索引,但和c1、c2、c3相关的,只是idx_test_01,c1作为前导列的复合索引,且c2和c3不在索引中,

CREATE TABLE test (
  id bigint(20) not null,
  c1 varchar(64) collate utf8_bin not null,
  c2 tinyint(4) not null,
  c3 bigint(20) default null,
  ...
  primary key(id),
  key idx_test_01(c1, ...)
  key ...
  ...
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

复制

显而易见,如上SQL执行时,能用到的索引就只有idx_test_01,Extra是NULL,

+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys | key         | key_len | ref  | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-------+
|  1 | SIMPLE      | test  | NULL       | index | idx_test_01   | idx_test_01 | 206     | NULL |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+---------------+-------------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

复制
我们知道,MySQL的索引默认是聚簇索引(可以理解为Oracle的IOT索引组织表),针对当前仅有(c1, ...)这个复合索引,当执行检索时,即便能使用这个复合索引,他都需要执行两个操作:(1)访问(c1, ...)复合索引;(2)从该复合索引中得到主键id,再进行回表,根据主键id,得到相应数据。这个过程中,最需要消耗的,就是磁盘IO的资源。不仅需要访问(c1, ...)复合索引的数据,还需要回表,访问数据行。
设计索引应该考虑到整个查询,不单只是WHERE条件。索引是能高效找到数据的方式,但是如果使用索引可以直接得到列的数据,即索引的叶子节点中已经包含要查询的数据,就无需回表,读数据行了。如果一个索引包含(或者叫做覆盖)所有要查询的字段的值,就可以称之为"覆盖索引",但是要注意,只有B-tree索引可以用于覆盖索引。
覆盖索引能显著提高检索的性能,原因就是查询只需要扫描索引而无需回表:
1. 索引条目通常远小于数据行大小,因此如果只需要扫描索引,就会极大地减少数据访问量。数据访问响应时间大部分花费在数据拷贝上,索引比数据更小,更容易全部放入内存中。
2. 因为索引是按照列值的顺序存储的,所以范围查询会比随机从磁盘读取每一行数据消耗的IO少得多。
3. 由于InnoDB的聚簇索引的特点,覆盖索引对InnoDB表特别有用,因为InnoDB的二级索引在叶子节点中保存了记录的主键值,所以如果二级索引能够覆盖查询,则可以避免对主键索引的二次查询。
在索引中满足查询的成本一般比查询记录本身要小得多。
因此,针对这条SQL,创建包含了(c1, c2, c3)的复合索引,
create index idx_test_02(c1, c2, c3) on test;

复制

此时执行SQL,Extra显示Using index,说明用到了覆盖索引的特性,

+----+-------------+-------+------------+-------+-------------------------+-------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys           | key         | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+-------------------------+-------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | test  | NULL       | index | idx_test_01,idx_test_02 | idx_test_02 | 204     | NULL |    1 |   100.00 | Using index |
+----+-------------+-------+------------+-------+-------------------------+-------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

复制
从执行效率上,原来跑55秒的语句,现在只需要2秒。
根据2-8原则,可能我们平时碰到的SQL优化,很多都可以用基础的知识解决,只有一小部分,需要一些技巧,或者更深层次的知识,但这些所谓的基础知识,"了解"和"理解",存在着区别,单从知识来讲,可能都知道是怎么个原理,但当碰到实际的场景,能不能将知识运用到实践中,就取决于对知识的理解程度了,这个不仅仅指数据库领域,其它任何领域,都是相通的,学习知识,重要的是能应用到实践中,能做到举一反三,这个的前提就是对知识是不是真正理解了,而不是停留在表面上。
因此,我们学习任何知识的时候,一定要强调理论和实践的结合,多积累经验,毕竟解决问题,才是我们大多数职场人学习的目标。

本文关键字#覆盖索引# #SQL性能优化#


文章推荐:

MySQL 中新建用户无法登陆的一种特殊场景

技术分享 | MySQL 的 MDL 锁解惑

技术分享 | MySQL 索引长度限制的案例

关于SQLE

可生开源社区的 SQLE 是一款面向数据库使用者和管理者,支持多场景审核,支持标准化上线流程,原生支持 MySQL 审核且数据库类型可扩展的 SQL 审核工具。

SQLE 获取
类型地址
版本库https://github.com/actiontech/sqle
文档https://actiontech.github.io/sqle-docs-cn/
发布信息https://github.com/actiontech/sqle/releases
数据审核插件开发文档https://actiontech.github.io/sqle-docs-cn/3.modules/3.7_auditplugin/auditplugin_development.html

更多关于 SQLE 的信息和交流,请加入官方QQ交流群:637150065...

点击阅读原文,详细了解SQLE

文章转载自爱可生开源社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论