vlambda博客
学习文章列表

聊聊MySQL几大索引类型:B-Tree索引,哈希索引,R-Tree空间数据索引,全文索引



概述

索引(在MySQL中也叫做“键(key)”)是存储引擎用于快速找到 记录的一种数据结构。这是索引的基本功能,除此之外,后面还将讨论 索引其他一些方面有用的属性。 

索引对于良好的性能非常关键。尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。在数据量较小且负载较低时,不恰当 的索引对性能的影响可能还不明显,但当数据量逐渐增大时,性能则会急剧下降(除非特别说明,我们假设使用的都是传统的硬盘驱动器。固态硬盘驱动器有着完全不同 的性能特性,本书将对此进行详细的描述。然而即使是固态硬盘,索引的原则依然成立,只是 那些需要尽量避免的糟糕索引对于固态硬盘的影响没有传统硬盘那么糟糕)。

不过,索引却经常被忽略,有时候甚至被误解,所以在实际案例中经常会遇到由糟糕索引导致的问题。 

索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高几个数量级,“最优”的索引有时比一个“好的”索引性能要 好两个数量级。创建一个真正“最优”的索引经常需要重写查询。




索引的基础

在MySQL中,存储引擎用类似的方法使用索引, 其先在索引中找到对应值,然后根据匹配的索引记录找到对应的数据行。
假如要运行下 面的查询:
mysql> SELECT first_name FROM sakila.actor WHERE actor_id=5;
如果在actor_id列上建有索引,则MySQL将使用该索引找到actor_id为5的行,也就是说,MySQL先在索引上按值进行查找,然后返回所有包含该值的数据行。 
索引可以包含一个或多个列的值。 如果索引包含多个列,那么列的顺序也十分重要,因为MySQL只能高效地使用索引的最左前缀列。创建一个包含两个列的索引,和创建两个只包含一列的索引是大不相同的,下面将详细介绍。


如果使用的是ORM,是否还需要关心索引?

简而言之:是的,仍然需要理解索引,即使是使用对象关系映射 (ORM)工具。 
ORM工具能够生产符合逻辑的、合法的查询(多数时候),除非 只是生成非常基本的查询(例如仅是根据主键查询),否则它很难生 成适合索引的查询。无论是多么复杂的ORM工具,在精妙和复杂的索 引面前都是“浮云”。读完后面的内容以后,你就会同意这个观 点的!很多时候,即使是查询优化技术专家也很难兼顾到各种情况, 更别说ORM了。





索引的类型

索引有很多种类型,可以为不同的场景提供更好的性能。在 MySQL中,索引是在存储引擎层而不是服务器层实现的。所以,并没有统一的索引标准:

  • 不同存储引擎的索引的工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引。
  • 即使多个存储引擎支持同一种 类型的索引,其底层的实现也可能不同。 
下面我们先来看看MySQL支持的索引类型,以及它们的优点和缺点。



B-Tree索引

当人们谈论索引的时候,如果没有特别指明类型,那多半说的是BTree索引,它使用B-Tree数据结构来存储数据(实际上很多存储引擎使用的是B+Tree,即每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历)。大多数MySQL引擎都支持这种索引。Archive引擎是一个例外:5.1之前Archive不支持任何索引,直到5.1才开始支持单个自增列(AUTO_INCREMENT)的索引。 

我们使用术语“B-Tree”,是因为MySQL在CREATE TABLE和其他语句中也使用该关键字。不过,底层的存储引擎也可能使用不同的存储结构,例如,NDB集群存储引擎内部实际上使用了T-Tree结构存储这种索引,即使其名字是BTREE;InnoDB则使用的是B+Tree。

存储引擎以不同的方式使用B-Tree索引,性能也各有不同,各有优劣。

  • 例如,MyISAM使用前缀压缩技术使得索引更小。

  • 但InnoDB则按照原数据格式进行存储。

  • 再如MyISAM索引通过数据的物理位置引用被索引的行,而InnoDB则根据主键引用被索引的行。 

B-Tree通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同。下图展示了B-Tree索引的抽象表示,大致反映了 InnoDB索引是如何工作的。MyISAM使用的结构有所不同,但基本思想是类似的。

B-Tree索引能够加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点(图示并未画出)开始进行搜索。根节点的槽中存放了指向子节点的指针,存储引擎根据这些指针向下层查找。通过比较节点页的值和要查找的值可以找到合适的指针进入下层子节点,这些指针实际上定义了子节点页中值的上限和下限。最终存储引擎要么是找到对应的值,要么该记录不存在。

叶子节点比较特别,它们的指针指向的是被索引的数据,而不是其他的节点页(不同引擎的“指针”类型不同)。上图仅绘制了一个节点和其对应的叶子节点,其实在根节点和叶子节点之间可能有很多层节点页。树的深度和表的大小直接相关。

B-Tree对索引列是顺序组织存储的,所以很适合查找范围数据。例如,在一个基于文本域的索引树上,按字母顺序传递连续的值进行查找是非常合适的,所以像“找出所有以1到K开头的名字”这样的查找效率会非常高。

假设有如下数据表:

CREATE TABLE People ( last_name varchar(50) not null, first_name varchar(50) not null, dob date not null, gender enum('m', 'f') not null, key(last_name, first_name, dob));
对于表中的每一行数据,索引中包含了last_name、frst_name和 dob列的值,下图显示了该索引是如何组织数据的存储的。

聊聊MySQL几大索引类型:B-Tree索引,哈希索引,R-Tree空间数据索引,全文索引

请注意,索引对多个值进行排序的依据是CREATE TABLE语句中定义索引时列的顺序。看一下最后两个条目,两个人的姓和名都一样, 则根据他们的出生日期来排列顺序。



可以使用B-Tree索引的查询类型

B-Tree索引适用于 全键值、键值范围或键前缀查找。其中键前缀查找只适用于根据最左前缀的查找(这是MySQL相关的特性,甚至和具体的版本也相关。其他有些数据库也可以使用索引 的非前缀部分,虽然使用完全的前缀的效率会更好。MySQL未来也可能会提供这个特性;后面也会介绍一些绕过限制的方法)。
前面所述的索引对如下类型的查询有效:
全值匹配:全值匹配指的是和索引中的所有列进行匹配,例如前面提到的索引可用于查找姓名为Cuba Allen、出生于1960-01-01的人。
匹配最左前缀:前面提到的索引可用于查找所有姓为Allen的人,即只使用索引的第一列。
匹配列前缀:也可以只匹配某一列的值的开头部分。例如前面提到的索引可用于查找所有以J开头的姓的人。这里也只使用了索引的第一列。
匹配范围值:例如前面提到的索引可用于查找姓在Allen和Barrymore之间的人。这里也只使用了索引的第一列。
精确匹配某一列并范围匹配另外一列:前面提到的索引也可用于查找所有姓为Allen,并且名字是字母K开头(比如Kim、Karl等)的人。即第一列last_name全匹配, 第二列frst_name范围匹配。
只访问索引的查询:B-Tree通常可以支持“只访问索引的查询”,即查询只需要访问索引,而无须访问数据行。后面我们将单独讨论这种“覆盖索引”的优化。



因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查询中的 ORDER BY操作(按顺序查找)。一般来说,如果B-Tree可以按照某种方式查找到值,那么也可以按照这种方式用于排序。所以,如果ORDER BY子句满足前面列出的几种查询类型,则这个索引也可以满足对应的排序需求。



B-Tree索引的限制

如果不是按照索引的最左列开始查找,则无法使用索引。例如上面 例子中的索引无法用于查找名字为Bill的人,也无法查找某个特定生日的人,因为这两列都 不是最左数据列。类似地,也无法查找姓氏以某个字母结尾的人。 
不能跳过索引中的列。也就是说,前面所述的索引无法用于查找姓 为Smith并且在某个特定日期出生的人。如果不指定名 (first_name),则MySQL只能使用索引的第一列。 
如果查询中有某个列的范围查询,则其右边所有列都无法使用索引 优化查找。例如有查询WHERE last_name='Smith' AND frst_name LIKE 'J%' AND dob='1976-12-23',这个查询只能使用索引的前两列,因为这里LIKE是一个范围条件(但是服务器可以把其余列用于其他目的)。如果范围查询列值的数量有限,那么可以通过使用多个等于条件来代替范围条件。在后面的索引案例学习部分,我们将演示一个详细的案例。”。


到这里读者应该可以明白,前面提到的索引列的顺序是多么的重要:这些限制都和索引列的顺序有关。在优化性能的时候,可能需要使用相同的列但顺序不同的索引来满足不同类型的查询需求。 

也有些限制并不是B-Tree本身导致的,而是MySQL优化器和存储引擎使用索引的方式导致的,这部分限制在未来的版本中可能就不再是限制了。




哈希索引

哈希索引(hash index)基于哈希表实现,只有精确匹配索引所有列的查询才有效。对于每一行数据,存储引擎都会对所有的索引列计算一个哈希码(hash code),哈希码是一个较小的值,并且不同键值的行计算出来的哈希码也不一样。哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存指向每个数据行的指针。

在MySQL中,只有Memory引擎显式支持哈希索引。这也是Memory 引擎表的默认索引类型,Memory引擎同时也支持B-Tree索引。值得一 提的是,Memory引擎是支持非唯一哈希索引的,这在数据库世界里面是比较与众不同的。如果多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希条目中。 

下面来看一个例子。假设有如下表:

CREATE TABLE testhash ( fname VARCHAR(50) NOT NULL, lname VARCHAR(50) NOT NULL, KEY USING HASH(fname)) ENGINE=MEMORY;
表中包含如下数据:

聊聊MySQL几大索引类型:B-Tree索引,哈希索引,R-Tree空间数据索引,全文索引

假设索引使用假想的哈希函数f(),它返回下面的值(都是示例数据,非真实数据):

f('Arjen')= 2323f('Baron')= 7437f('Peter')= 8784f('Vadim')= 2458

则哈希索引的数据结构如下:

聊聊MySQL几大索引类型:B-Tree索引,哈希索引,R-Tree空间数据索引,全文索引

注意每个槽的编号是顺序的,但是数据行不是。现在,来看如下查 询:

mysql> SELECT lname FROM testhash WHERE fname='Peter';

MySQL先计算'Peter'的哈希值,并使用该值寻找对应的记录指针。因为f('Peter')=8784,所以MySQL在索引中查找8784,可以找到指向第3行的指针,最后一步是比较第三行的值是否为'Peter',以确保就是要查找的行。




哈希索引的限制

因为索引自身只需存储对应的哈希值,所以索引的结构十分紧凑, 这也让哈希索引查找的速度非常快。
然而,哈希索引也 有它的限制:
哈希索引 只包含哈希值和行指针,而不存储字段值,所以不能使用 索引中的值来避免读取行。不过,访问内存中的行的速度很快,所 以大部分情况下这一点对性能的影响并不明显。 
哈希索引数据并 不是按照索引值顺序存储的,所以也就无法用于排 序。
哈希索引也 不支持部分索引列匹配查找,因为哈希索引始终是使用 索引列的全部内容来计算哈希值的。例如,在数据列(A,B)上建 立哈希索引,如果查询只有数据列A,则无法使用该索引。
哈希索引只支持等值比较查询,包括=、IN()、<=>(注意<>和<=> 是不同的操作)。也不支持任何范围查询,例如WHERE price>100。
访问哈希索引的数据非常快,除非有很多哈希冲突(不同的索引列 值却有相同的哈希值)。当 出现哈希冲突的时候,存储引擎必须 遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。 
如果哈希冲突很多的话,一些 索引维护操作的代价也会很高。例 如,如果在某个选择性很低(哈希冲突很多)的列上建立哈希索引,那么当从表中删除一行时,存储引擎需要遍历对应哈希值的链表中的每一行,找到并删除对应行的引用,冲突越多,代价越大。



因为这些限制,哈希索引只适用于某些特定的场合。而一旦适合哈希索引,则它带来的性能提升将非常显著。举个例子,在数据仓库应用中有一种经典的“星型”schema,需要关联很多查找表,哈希索引就非常适合查找表的需求。

除了Memory引擎外,NDB集群引擎也支持唯一哈希索引,且在 NDB集群引擎中作用非常特殊。

InnoDB引擎有一个特殊的功能叫做“自适应哈希索引(adaptive hash index)”。当InnoDB注意到某些索引值被使用得非常频繁时,它会在内存中基于B-Tree索引之上再创建一个哈希索引,这样就让B-Tree索引也具有哈希索引的一些优点,比如快速的哈希查找。这是一个完全自动的、内部的行为,用户无法控制或者配置,不过如果有必要,完全可以关闭该功能。



创建自定义哈希索引

如果存储引擎不支持哈希索引,则可以模拟像InnoDB一样创建哈希索引,这可以享受一些 哈希索引的便利,例如只需要很小的索引就可以为超长的键创建索引。 
思路很简单:在B-Tree基础上创建一个 伪哈希索引。这和真正的哈 希索引不是一回事,因为还是使用B-Tree进行查找,但是它使用哈希值 而不是键本身进行索引查找。你需要做的就是在查询的WHERE子句中手动 指定使用哈希函数
下面是一个实例,例如需要存储大量的URL,并需要根据URL进行 搜索查找。如果使用B-Tree来存储URL,存储的内容就会很大,因为 URL本身都很长。正常情况下会有如下查询:
mysql> SELECT id FROM url WHERE url="http://www.mysql.com";
若删除原来URL列上的索引,而 新增一个被索引的url_crc列,使 用CRC32做哈希,就可以使用下面的方式查询:
mysql> SELECT id FROM url WHERE url="http://www.mysql.com"-> AND url_crc=CRC32("http://www.mysql.com");
这样做的 性能会非常高,因为MySQL优化器会使用这个选择性很 高而体积很小的基于url_crc列的索引来完成查找(在上面的案例中, 索引值为1560514994)。即使有多个记录有相同的索引值,查找仍然很快,只需要根据哈希值做快速的整数比较就能找到索引条目,然后一一 比较返回对应的行。另外一种方式就是对完整的URL字符串做索引,那 样会非常慢。
这样实现的缺陷是需要 维护哈希值。可以手动维护,也可以使用触 发器实现。下面的案例演示了触发器如何在插入和更新时维护url_crc 列。
首先 创建如下表:
CREATE TABLE pseudohash ( id int unsigned NOT NULL auto_increment, url varchar(255) NOT NULL, url_crc int unsigned NOT NULL DEFAULT 0, PRIMARY KEY(id));
然后创建触发器。先临时修改一下语句分隔符,这样就可以在触发 器定义中使用分号:
DELIMITER //CREATE TRIGGER pseudohash_crc_ins BEFORE INSERT ON pseudohash FOR EACH ROW BEGINSET NEW.url_crc=crc32(NEW.url);END;//CREATE TRIGGER pseudohash_crc_upd BEFORE UPDATE ON pseudohash FOR EACH ROW BEGINSET NEW.url_crc=crc32(NEW.url);END;//DELIMITER ;
剩下的工作就是验证一下触发器 如何维护哈希索引:

聊聊MySQL几大索引类型:B-Tree索引,哈希索引,R-Tree空间数据索引,全文索引

如果采用这种方式,记住 不要使用SHA1()和MD5()作为哈希函数。因为这两个函数计算出来的哈希值是 非常长的字符串,会浪费大量空间,比较时也会更慢。SHA1()和MD5()是强加密函数,设计目标是最大限度消除冲突,但这里并不需要这样高的要求。简单哈希函数的冲突在 一个可以接受的范围,同时又能够提供更好的性能。
如果数据表非常大,CRC32()会出现大量的哈希冲突,则可以考虑 自己实现一个简单的64位哈希函数。这个自定义函数要返回整数,而不是字符串。一个简单的办法可以使用MD5()函数返回值的一部分来作为自定义哈希函数。这可能比自己写一个哈希算法的性能要差,不过这样实现最简单:

聊聊MySQL几大索引类型:B-Tree索引,哈希索引,R-Tree空间数据索引,全文索引






处理哈希冲突

当使用哈希索引进行查询的时候,必须在WHERE 子句中包含 常量值:
mysql> SELECT id FROM url WHERE url_crc=CRC32("http://www.mysql.com")-> AND url="http://www.mysql.com";
一旦出现哈希冲突,另一个字符串的哈希值也恰好是1560514994, 则下面的查询是 无法正确工作的
mysql> SELECT id FROM url WHERE url_crc=CRC32("http://www.mysql.com");
因为所谓的“生日悖论” (参考http://en.wikipedia.org/wiki/Birthday_problem),出现哈希冲突的概率的 增长速度可能比想象的要快得多。CRC32()返回的是32位的整数,当索引有93000条记录时出现冲突的概率是1%。例如我们将/usr/share/dict/words中的词导入数据表并进行CRC32()计算,最后会有98569行。这就已经出现一次哈希冲突了,冲突让下面的查询返回了多条记录:

聊聊MySQL几大索引类型:B-Tree索引,哈希索引,R-Tree空间数据索引,全文索引

正确的写法应该如下:

要避免冲突问题,必须在WHERE条件中 带入哈希值和对应列值。如 果不是想查询具体值,例如只是统计记录数(不精确的),则可以不带入列值,直接使用CRC32()的哈希值查询即可。还可以使用如FNV64()函数作为哈希函数,这是移植自Percona Server的函数,可以以插件的方式在任何MySQL版本中使用,哈希值为64位,速度快,且冲突比CRC32()要少很多。





空间数据索引(R-Tree)

MyISAM表支持空间索引,可以用作地理数据存储。

和B-Tree索引不同,这类索引无须前缀查询。空间索引会从所有维度来索引数据。查询时,可以有效地使用任意维度来组合查询。必须使用MySQL的GIS相关函数如MBRCONTAINS()等来维护数据。

MySQL的GIS支持并不完善,所以大部分人都不会使用这个特性。开源关系数据库系统中对GIS的解决方案做得比较好的是PostgreSQL的PostGIS。




全文索引

全文索引是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。

全文搜索和其他几类索引的匹配方式完全不一样它有许多需要注意的细节,如停用词、词干和复数、布尔搜索等。全文索引更类似于搜索引擎做的事情,而不是简单的WHERE条件匹配。

在相同的列上同时创建全文索引和基于值的B-Tree索引不会有冲突,全文索引适用于MATCH AGAINST操作,而不是普通的WHERE条件操作。

我们会在后面的章节讨论更多的全文索引的细节。




其他索引类型

还有很多第三方的存储引擎使用不同类型的数据结构来存储索引。例如TokuDB使用分形树索引(fractal tree index),这是一类较新开发的数据结构,既有B-Tree的很多优点,也避免了B-Tree的一些缺点。如 果通读完本章,可以看到很多关于InnoDB的主题,包括聚簇索引、覆 盖索引等。多数情况下,针对InnoDB的讨论也都适用于TokuDB。

ScaleDB使用Patricia tries(这个词不是拼写错误),其他一些存储 引擎技术如InfiniDB和Infobright则使用了一些特殊的数据结构来优化某 些特殊的查询。




索引的优点

索引可以让服务器快速地定位到表的指定位置。但是这并不是索引的唯一作用,到目前为止可以看到,根据创建索引的数据结构不同,索 引也有一些其他的附加作用

最常见的B-Tree索引,按照顺序存储数据,所以MySQL可以用来做ORDER BY和GROUP BY操作。因为数据是有序的,所以B-Tree也就会将相关的列值都存储在一起。最后,因为索引中存储了实际的列值,所以某些查询只使用索引就能够完成全部查询。据此特性,总结下来索引有如下三个优点: 

  • 1. 索引大大减少了服务器需要扫描的数据量。

  • 2. 索引可以帮助服务器避免排序和临时表。

  • 3. 索引可以将随机I/O变为顺序I/O。



“索引”这个主题完全值得单独写一本书,如果想深入理解这部分内容,强烈建议阅读由Tapio Lahdenmaki和Mike Leach编写的Relational Database Index Design and the Optimizers(Wiley出版社)一书,该书详细介绍了如何计算索引的成本和作用、如何评估查询速度、如何分析索引维护的代价和其带来的好处等。
Lahdenmaki和Leach在书中介绍了如何评价一个索引是否适合某个查询的 “三星系统”(three-star system):
  • 索引将相关的记录放到一起则获得一星;

  • 如果索引中的数据顺序和查找中的排列顺序一致则获得二星;

  • 如果索引中的列包含了查询中需要的全部列则获得“三星”。

后面我们将会介绍这些原则。




索引是最好的解决方案吗?

索引并不总是最好的工具。总的来说, 只有当索引帮助存储引擎 快速查找到记录带来的好处大于其带来的额外工作时,索引才是有效 的。
对于非常小的表,大部分情况下简单的全表扫描更高效。 对于中 到大型的表,索引就非常有效。 但对于特大型的表,建立和使用索引 的代价将随之增长。这种情况下,则需要一种技术可以直接区分出查 询需要的一组数据,而不是一条记录一条记录地匹配。例如可以使用 分区技术,后面会介绍。
如果表的数量特别多,可以建立一个 元数据信息表,用来查询需 要用到的某些特性。例如执行那些需要聚合多个应用分布在多个表的 数据的查询,则需要记录“哪个用户的信息存储在哪个表中”的元数据,这样在查询时就可以直接忽略那些不包含指定用户信息的表。对于大型系统,这是一个常用的技巧。事实上,Infobright就是使用类似的实现。对于TB级别的数据,定位单条记录的意义不大,所以经常会使用块级别元数据技术来替代索引。





索引总结

这里的总结也包括了后面几篇索引文章的学习总结。

在MySQL中,大多数情况下都会使用B-Tree索引。其他类型的索引大多只适用于特殊的目的。如果在合适的场景中使用索引,将大大提高查询的响应时间。




三个原则

在选择索引和编写利用这些索引的查询时,有如下三个原则始终需 要记住:
1. 单行访问是很慢的 。特别是在机械硬盘存储中(SSD的随机I/O要快 很多,不过这一点仍然成立)。如果服务器从存储中读取一个数据 块只是为了获取其中一行,那么就浪费了很多工作。最好读取的块 中能包含尽可能多所需要的行。使用索引可以创建位置引用以提升 效率。
2. 按顺序访问范围数据是很快的,这有两个原因 。第一,顺序I/O不 需要多次磁盘寻道,所以比随机I/O要快很多(特别是对机械硬 盘)。第二,如果服务器能够按需要顺序读取数据,那么就不再需要额外的排序操作,并且GROUP BY查询也无须再做排序和将行按组 进行聚合计算了。
3. 索引覆盖查询是很快的。 如果一个索引包含了查询需要的所有列, 那么存储引擎就不需要再回表查找行。这避免了大量的单行访问, 而上面的第1点已经写明单行访问是很慢的。



总的来说,编写查询语句时应该尽可能选择合适的索引以避免单行查找、尽可能地使用数据原生顺序从而避免额外的排序操作,并尽可能使用索引覆盖查询。这与前面提到的Lahdenmaki和Leach的书中 的“三星”评价系统是一致的。

如果表上的每一个查询都能有一个完美的索引来满足当然是最好的。但不幸的是,要这么做有时可能需要创建大量的索引。还有一些时候对某些查询是不可能创建一个达到“三星”的索引的(例如查询要按照 两个列排序,其中一个列正序,另一个列倒序)。这时必须有所取舍以 创建最合适的索引,或者寻求替代策略(例如反范式化,或者提前计算 汇总表等)。

理解索引是如何工作的非常重要,应该根据这些理解来创建最合适的索引,而不是根据一些诸如“在多列索引中将选择性最高的列放在第 一列”或“应该为WHERE子句中出现的所有列创建索引”之类的经验法则 及其推论。

那如何判断一个系统创建的索引是合理的呢?一般来说,我们建议按响应时间来对查询进行分析。找出那些消耗最长时间的查询或者那些给服务器带来最大压力的查询,然后检查这些查询的schema、SQL和索引结构,判断是否有查询扫描了太多的行,是否做了很多额外的排序或者使用了临时表,是否使用随机I/O访 问数据,或者是有太多回表查询那些不在索引中的列的操作。

如果一个查询无法从所有可能的索引中获益,则应该看看是否可以创建一个更合适的索引来提升性能。如果不行,也可以看看是否可以重写该查询,将其转化成一个能够高效利用现有索引或者新创建索引的查询。这也是下一章要介绍的内容。

如果根据基于响应时间的分析不能找出有问题的查询呢?是否可能有我们没有注意到的“很糟糕”的查询,需要一个更好的索 引来获取更高的性能?一般来说,不可能。对于诊断时抓不到的查询, 那就不是问题。但是,这个查询未来有可能会成为问题,因为应用程 序、数据和负载都在变化。如果仍然想找到那些索引不是很合适的查 询,并在它们成为问题前进行优化,则可以使用pt-query-digest的查询审 查“review”功能,分析其EXPLAIN出来的执行计划。



多栖技术控小董
内心永远年轻,永远奋斗,一个热爱技术的90后程序员!
19篇原创内容
Official Account