MySQL覆盖索引优化案例一则

Posted bisal(Chen Liu)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL覆盖索引优化案例一则相关的知识,希望对你有一定的参考价值。

最近有个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优化,很多都可以用基础的知识解决,只有一小部分,需要一些技巧,或者更深层次的知识,但这些所谓的基础知识,"了解"和"理解",存在着区别,单从知识来讲,可能都知道是怎么个原理,但当碰到实际的场景,能不能将知识运用到实践中,就取决于对知识的理解程度了,这个不仅仅指数据库领域,其它任何领域,都是相通的,学习知识,重要的是能应用到实践中,能做到举一反三,这个的前提就是对知识是不是真正理解了,而不是停留在表面上。

因此,我们学习任何知识的时候,一定要强调理论和实践的结合,多积累经验,毕竟解决问题,才是我们大多数职场人学习的目标。

如果您认为这篇文章有些帮助,还请不吝点下文章末尾的"点赞"和"在看",或者直接转发pyq,

近期更新的文章:

一个困扰许久的Word嵌入文档问题(求助)

蹭热度,了解一下全面实行的股票发行注册制改革

推荐一篇Oracle RAC Cache Fusion的经典论文

Java解析XML的实践

最近碰到的一些问题

近期的热文:

"红警"游戏开源代码带给我们的震撼

文章分类和索引:

公众号1100篇文章分类和索引

以上是关于MySQL覆盖索引优化案例一则的主要内容,如果未能解决你的问题,请参考以下文章

小白学习MySQL - 变通创建索引的案例一则

MySQL隐式转换案例一则

Mysql优化索引覆盖

MySQL 优化器之Index merge Multi-Range Read MRR与Batched Key Access使用案例详解

mysql--索引优化

Mysql性能优化:为什么要用覆盖索引?