登录/注册
fuyao-w
23
0
0
浏览量
粉丝
关注
索引优化
fuyao-w
2020-11-26 15:34:53
21
0

良好的schema设计原则是普遍适用的,但 MySQL 有它自己的实现细节要注意。概括来说,尽可能保持任何东西小而简答总是最好的。MySQL 更喜欢简单,需要使用数据库的人也应该会同样喜欢简单的原则:

  • 尽量避免过度的设计,例如会导致及其复杂查询的schema设计,或者有很多列的表设计。
  • 使用小而简单的合适数据类型,除非真实数据模型中有确切需要,否则应该尽可能避免使用NULLL值。
  • 尽量使用相同的数据类型存储相同或相关的值,尤其是在关联条件中使用的列。
  • 注意可变长字符串,其在临时表和排序时,可能导致悲观的按最大长度分配内存。
  • 尽量使用整形定义表示列。
  • 避免使用MySQL 已经遗弃的特性,例如指定浮点数的精度,或者整数的显示宽度。
  • 小心使用 ENUM 和 SET 。虽然他们用起来很方便,但是不要滥用,否则有时候会变成陷阱。最好避免使用 BIT。
  • 范式是好的,但是反范式(大多数情况下意味着重复数据)有时也是必须的,并且能带来好处。
  • 最后 ALERT TABLE 在大部分情况下,它都会锁表并且重建整张表。

创建高性能的索引

索引(在 MySQL 中也叫"键"(key))是存储引擎用于快速找到记录的一种数据结构,这是索引的基本功能。索引对于良好的性能来说非常关键,尤其是当表中的数据量越来越大的时候,索引对于性能的影响愈发重要。在数据量较小且负载较低时,不恰当的索引对性能的影响可能还不明显,但当数据量逐渐增大时,性能会急剧下降。

索引优化应该是对查询性能优化最有效的手段了。索引能够将查询性能提高几个数量级。

索引基础

索引可以包含一个或多个列的值。如果索引包含多个列,那么列的顺序也十分重要,因为MySQL 只能有效地使用索引的最左前缀列。创建一个包含两个列的索引,和创建两个各包含一个列的索引大不相同。

索引的类型

在MySQL 中,索引是在存储引擎层而不是服务器层实现的。所以没有统一的索引标准:不同存储引擎的索引的工作方式是不一样的,也不是所有的存储引擎都支持所有类型的索引。即使多个存储引擎支持相同的索引,其底层实现原理也可能不同。

B-Tree 索引

B - Tree索引使用 B-Tree 数据结构来存储数据。大多数MySQL 存储引擎都支持这种索引。Archive 引擎是个例外:5.1 之前 Archive 引擎不支持任何索引,知道5.1 之后才支持单个自增列的索引。

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

存储引擎以不同的方式使用B-Tree索引,性能也各有不同,各有优势。例如,MyISAM使用前缀压缩技术使得索引更小,但InnoDB 则按照原数据格式进行存储。再如 MyISAM 索引通过数据的物理位置引用被索引的行,而InnoDB 使用主键引用被索引的行。

B-Tree 通常意味着所有的值都是按顺序存储的,并且根到每个叶子节点的距离相同。

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

叶子节点比较特别,他们的指针指向的是被索引的数据,而不是其他的节点页(不同类型的引擎“指针”类型不同)。

B-Tree 对索引列是顺序组织存储的,所以很合适查找范围数据。例如,在一个基于文本域的索引树上,按字母顺序传递连续的值,进行查找是非常合适的。

注意,索引对多个值进行排序的依据是 CREATE TABLE 语句中定义索引时列的顺序。

可以使用 B-Tree 索引的查询类型。B-Tree 索引适用于全键值,键值范围或键前缀查找。其中键前缀查找只适用于根据最左前缀的查找。前面所述的索引对如下类型的查询有效:

全值匹配

全值匹配指的是和索引中所有列进行匹配,

匹配最左前缀

对于多个列组成的索引,可以只使用索引的前几列。

匹配列前缀

也可以只匹配某一列的值的开头部分。例如对于索引第一列为姓的列,可以查找所有以K 开头的姓的人,这里也只使用了索引的第一列。

匹配范围值

上面提到的索引可以用于查找姓在 Aleen 和 blob之间的人。这里也只是用了索引的第一列。

精确匹配某一列并范围匹配另外一列

前面提到的索引的第二列为名,也可用于查找所有姓为 Aleen,并且名子是K 开头的人。即第一列姓全民匹配,第二列名范围匹配。

只访问索引的查询

B-Tree 通常可以支持“只访问索引的查询”,即查询只需要访问索引,而无需访问数据行。

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

下面是关于B-Tree的一些限制:

  • 如果不是按照索引的最左开始查找,则无法使用索引。
  • 不能跳过索引中的列。
  • 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查找。

索引的顺序是非常重要的,这些限制都和索引的列有关。在优化性能的时候,可能需要使用相同的列但顺序不同的索引来满足不同类型的查询需求。

哈希索引

哈希索引是基于哈希表的实现,只有精确匹配索引的所有列才有效。对于每一行数据,存储引擎都会对所有的索引列计算下一个哈希码,哈希索引将所有的哈希码存储在索引中,同时在哈希表中,保存指向每个数据行的指针。

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

因为索引自身只需存储相应的哈希值,所以索引的结构十分紧凑,这也让Hash索引的速度非常快。然而哈希索引也有它的限制:

  • 哈希索引只包含哈希值和行指针,而不存储字段值,索引不能通过读取索引中的值来避免读取行。不过访问内存中的行的速度很快,所以大部分情况下这一点对性能的影响并不明显。
  • 哈希索引数据不是按照索引值顺序存储的,所以也就无法用于排序。
  • 哈希索引也不支持部分索引匹配列查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值的。
  • 哈希索引只支持等值比较查询,包括 = 、IN()、<=> 也不支持任何范围查询。
  • 访问哈希索引的数据非常快,除非有很多的哈希冲突,当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行查找,知道找到所有符合条件的行。
  • 如果哈希冲突很多的话,一些索引维护操作的代价也会很高。

因为这些限制,哈希索引只使用与某些特定的场合。而一旦适合哈希索引,则他带来的性能将显著提升。

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

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

创建自定义哈希索引。如果存储引擎不支持哈希索引,则可以模拟像InnoDB 一样创建哈希索引,这可以享受一些哈希索引的便利,例如可以为超长的键创建很小的索引。

思路:在B-Tree 基础上创建一个伪哈希索引。这和真正的哈希索引不是一回事,因为还是在使用 B-Tree 进行查找。需要做的就是在查询的 WHERE 子句中手动指定哈希函数。

空间数据索引

MyISAM 表支持空间索引,可以用作地理数据存储,和B-Tree索引不同,这类索引无须前缀查询。空间索引会从所有维度来索引数据。查询时,可以使用有效地使用任意维度来组合查询。必须使用MySQL 的 GIS 相关函数来维护数据。

全文索引

全文索引是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值。全文索引和其他几类索引的匹配方式完全不一样。它有许多需要注意的细节,如停用词、词干和复数、布尔搜索等。全文搜索引擎类似于搜索引擎做的事情,而不是简单的 WHERE 条件匹配。

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

索引的优点

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

高性能的索引策略

独立的列

“独立的列”是指索引不能是表达式的一部分,也不能是函数的参数。

例如:

SELECT id FROM actor WHERE id +1 = 5

SELECT ... WHERE TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col) <= 10

前缀索引和索引选择性

有时候需要索引很长的字符列,这回让索引变得大且慢。一个策略是前面提到过的模拟哈希索引。但有时候这样做还不够。

通常可以索引开始部分字符,这样可以大大节约索引空间,从而提高索引效率。但这样也会降低索引的选择性。索引的选择性是指,不重复的索引值(也成为基数),和数据表的记录总数(T)的比值,范围从 1/T 到 1 之间,索引的选择性越高则查询效率越高,因为选择性高的索引可以让MySQL 在查找时过滤掉更多的行。唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

一般情况下,某个列前缀的选择性也是足够高的,足以满足查询性能,对于 BLOB、TEXT 或者很长的 VARCHAR 类型的列,必须使用前缀索引,因为MySQL 不允许索引这些列的完整长度。

诀窍在于要选择足够长的前缀以保证高的选择性,同时又不能太长(以便节约空间)。前缀应该足够长,以使得前缀索引的选择性接近于索引整个列。换句话说,前缀的“基数” 应该接近于完整列的“基数”。

多列索引

聚簇索引

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。具体的细节依赖于其实现方式,但InnoDB 的聚簇索引实际上在同一个结构中保存了 B-Tree索引和数据行。

当表有聚簇索引时,它的数据行实际上存放在索引的叶子页(leaf page)中。术语”聚簇“表示数据行和相邻的键值紧凑地存储在一起。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引(不过,覆盖索引可以模拟多个聚簇索引的情况)。

因为存储引擎负责实现索引,因此不是所有的存储引擎都支持聚簇索引。我们主要关注 InnoDB,但是这里讨论的原理对于任何支持聚簇的引擎都是适用的。InnoDB 通过主键聚集数据。被索引的列就是主键列。

如果没有定义主键,InnoDB 会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB 会隐式定义一个主键来作为聚簇索引。InnoDB 只聚集在同一个页面中的记录。包含相邻键值的页面可能会相距甚远。

聚簇主键可能对性能有帮助,但也可能导致严重的性能问题。所以需要仔细地考虑聚簇索引,尤其是将表的存储引擎从InnoDB 改成其他引擎的时候。

聚集的数据有一些重要的优点:

  • 可以把相关数据保存在一起。例如实现电子邮箱时,可以根据用户ID来聚集数据,这样只需要从磁盘读取少数的数据页就能获取某个用户的全部邮件。如果没有使用聚簇索引,则每封邮件都可能导致一次磁盘I/O。
  • 数据访问更快,聚簇索引将索引和数据保存在同一个 B-Tree 中,因此从聚簇索引中获取数据通常比在非聚簇索引中查找要快。
  • 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。

如果在设计表和查询时能充分利用上面的优点,那就能极大地提升性能。同时聚簇索引也有一些缺点。

  • 聚簇数据最大限度地提高了I/O 密集型应用的性能,但如果数据全部都放在内存中,则访问的顺序就没那么重要了,聚簇索引也就没什么优势了。
  • 插入速度严重依赖于插入顺序。按照主键的顺序插入是加载数据到 InnoDB 表速度最快的方式。但如果不是按照主键顺序加载数据,那么在加载完后最好使用 OPTIMIZE TABLE 命令重新组织一下表。
  • 更新聚簇索引列的代价很高,因为会强制InnoDB 将每个被更新的行移动到新的位置。
  • 基于据需索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临“页分裂”的问题。当行的主键值要求必须将这一行插入到某个已满的页中时,存储引擎会将该页分裂成两个页面来容纳该行,这就是一次页分裂操作。页分裂会导致表占用更多的磁盘空间。
  • 聚簇索引可能导致全表扫面变慢,尤其是行比较稀疏,或者由于页分裂呆滞数据存储不连续的时候。
  • 二级索引(非聚簇索引)可能比想象的要更大,因为在二级索引的叶子节点包含了引用行的主键列。
  • 二级索引访问需要两次索引查找,而不是一次。

二级索引(使用非主键创建的索引)叶子节点保存的是主键值,而不是指向行的物理位置指针。这意味着通过二级索引查找行,存储引擎需要找到二级索引的叶子节点获得对应的主键值,然后根据这个值去取出索引中查找到对应的行。这里做了重复的工作:两次 B-Tree 查找而不是一次。对于哈希索引,自适应性哈希索引能够减少这样的重复工作。

InnoDB 和 MyIASM 的数据分布对比

MyISAM 的数据分布非常简单,按照数据的插入顺序存储在磁盘上。在每行的旁边显示了行号,从0开始递增。如果行是定长的,所以MyISAM 可以从表的开头跳过所需的字节找到需要的行。(MyISAM 并不总是使用行号,而是根据定长还是边长使用不同的策略)。

MyISAM 上的主键索引和其他索引没有什么不同,叶子节点的指针指向了数据所在的行号,只不过主键索引就是一个名为 PRIMARY 的唯一非空索引。

InnoDB 的数据分布

因为InnoDB 支持聚簇索引,所以使用非常不同的方式存储同样的数据。在InnoDB 中,聚簇索引就是整张表,所以不想MyISAM 那样需要独立的行存储。

聚簇索引的每个叶子节点都包含了主键值、事物ID、用于事物和MVCC 的回滚指针以及所有的剩余列。如果主键是一个列前缀索引,InnoDB 也会包含完整的主键列和剩下的其他列。

还有一点和 MyISAM 不同的是,InnoDB 的耳机索引和聚簇索引很不相同。InnoDB 二级索引的叶子节点中存储的不是“行指针”,而是主键值,并以此作为指向行的“指针”。这样的策略减少了当出现行移动或者数据页分裂时二级索引的维护工作。使用主键值当做指针会让二级索引占用更多的空间,换来的好处是,InnoDB 在移动行时无需更新二级索引中的这个“指针”。

在InnoDB 表中按主键顺序插入行

如果正在使用InnoDB 表并没有什么数据需要聚集,那么可以定义一个代理主键作为主键。这种主键的数据应该和应用无关,最简单的方法是使用 AUTO_INCREMENT 自增列。这样可以保证数据行是按顺序写入,对于根据主键做关联操作的性能也会更好。

最好避免随机的(不连续且值的分布范围非常大)聚簇索引,特别是对于I/O 秘籍型的应用。例如,从性能的角度考虑,使用UUID 来作为聚簇索引则会很糟糕:它使得聚簇索引的插入变得完全随机,这是最坏的情况,使得数据没有任何聚集特性。

在使用连续有序主键的索引时,每插入一个数据,InnoDB 把每一条记录都存储在上一条记录的后面。当达到页的最大填充因子时(InnoDB 默认的最大填充因子是页大小的 15/16 ,留出部分空间用于以后修改),下一条记录就会写入新的页中。一旦数据按照这种顺序的方式加载,主键页就会近似于被顺序的记录填满。

但是如果使用随机的聚簇索引的表插入数据,因为新行的主键值不一定比之前插入的大,所以 InnoDB 无法简单地总是把新行插入到索引的最后,而是需要为新的行寻找合适的位置——通常是已有数据的中间位置——并且分配空间。这回增加很多额外的工作,并导致数据分布不够优化。带来一些缺点:

  • 写入的目标页可能已经被写入到磁盘上并从缓存中移除,或者是还没有被加载到缓存中,InnoDB 在插入之前不得不先找到并从磁盘读取目标页到内存中。这将导致大量额随机 I/O。
  • 因为写入是乱序的,InnoDB 不得不频繁地做分裂操作,以便为新的行分配空间。页分裂会导致移动大量数据,一次插入最少需要修改三个页而不是一个页。
  • 由于频繁地页分裂,页会变得稀疏并被不规则地填充,所以最终数据会有碎片。

在把这些随机值载入到聚簇索引以后,也许需要做一次 OPTIMIZE TABLE 来重建表并优化页的填充。

覆盖索引

通常大家都会根据查询的 WHERE 条件来创建合适的索引,不过这只是索引优化的一个方面。设计优秀的索引应该考虑到整个查询,而不单单是 WHERE 部分。MySQL 也可以使用索引来直接获取列的数据,这样就不需要读取数据行。如果索引的叶子节点中已经包含需要查询的数据,那么就不用在回表查询了。如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。

覆盖索引是非常有用的工具,能够及大地提高性能。覆盖索引带来的好处:

  • 索引条目通常远小于数据行大小,所以如果只需要读取索引,那 MySQL 就会及大地减少数据访问量。这对缓存的负载非常重要,因为这种情况下响应时间大部分花费在数据拷贝上。覆盖索引对于I/O密集型的应用也有帮助,因为索引比数据更小,更容易放入内存中(这对于 MyISAM 尤其正确,因为 MyISAM 能压缩索引以变得更小)。
  • 因为索引是按照列值顺序存储的(至少在单个页内是如此),所以对于I/O 密集型的范围查询会比随机从磁盘读取每一行数据的I/O要少的多。对于某些存储引擎,例如MyISAM 和 Percona XtraDB ,甚至可以通过 OPTIMIZE 命令是的索引完全顺序排列,这让简单的范围查询能使用完全顺序的索引访问。
  • 一些存储引擎如 MyISAM 在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用。这可能会导致严重的性能问题,尤其是那些系统调用占了数据访问中的最大开销的场景。
  • 由于InnoDB 的聚簇索引,覆盖索引对InnoDB表特别有用。InnoDB 的二级索引在叶子节点中保存了行的主键值,所以如果二级主键能够覆盖查询,则可以避免对主键索引的二次查询。

在所有的这些场景中,在索引中满足查询的成本一般比查询行小的多。

因为覆盖索引必须存储索引列的值,而哈希索引、空间索引和全文索引等都不存储索引列的值,所以MySQL 只能使用 B-Tree 索引做覆盖索引。另外,不同的存储引擎覆盖索引的方式不同,而且不是所有的引擎都支持覆盖索引。

当发起一个被索引覆盖的查询时,在 EXPLAIN 的 EXTRA 列可以看到 “Using index” 的信息。

索引覆盖查询还有很多陷阱可能会导致无法实现优化。MySQL 查询优化器会在执行查询前判断是够有一个索引能进行覆盖。假设索引覆盖了 WHERE 条件中的字段,但不是整个查询设计的字段。如果条件为假(false),MySQL 5.5 和更早的版本也总会是回到表获取数据行,尽管并不需要这一行且最终会被过滤掉。

使用索引扫描来做排序

MySQL 有两种方式可以生成有序的结果:通过排序操作;或者按索引顺序扫描;如果EXPLAIN 出来的 type 列的值为“index” ,则说明MySQL使用了索引扫描来做排序。

扫描索引本身是很快的,因为只需要从一条索引记录移动到紧接着的下一条记录。但如果索引不能覆盖查询所需的全部列,那就不得不每扫描一条记录就都回表查询一次对应的行。这基本上都是随机I/O ,因此按索引顺序读取数据的速度通常要比顺序地全表扫描慢,尤其是在 I/O 密集型的工作负载时。

MySQL 可以使用同一个索引既满足排序,又用于查找行。因此,如果可能,设计索引时应该尽可能地同时满足这两种任务,这样是最好的。

只有当索引的列顺序和 ORDER BY 子句的顺序完全一致,并且所有列的排序方向都一样时,MySQL 才能够使用索引来对结果做排序。如果查询需要关联多张表,则只有当 ORDER BY 子句引用的字段全部为第一张表时,才能够使用索引做排序。ORDER BY 子句和查找型查询的限制是一样的:需要满足索引的最左前缀的要求;否则,MySQL 都需要执行排序操作,而无法利用索引排序。

有一种情况下 ORDER BY 子句可以不满足索引的最左前缀要求,就是前导列为常量的时候。如果 WHERE 子句或者 JOIN 自己中对这些列指定了常量,就可以弥补索引的不足。

例如:下面这句为 rental_data ,inventory_id,customer_id 建立唯一索引

SELECT rentail_id,staff_id FROM sakila.rental WHERE rental_data = "2005-05-05"

ORDER BY inventory_id ,customer_id

因为索引的第一个列被指定为常数,所及即使不满足索引的最左前缀的要求,也可以用于查询排序。

压缩(前缀压缩)索引

MyISAM 使用前缀压缩来减少索引的大小,从而让更多的索引可以放入内存中,这在某些情况下可能及大地提高性能。默认只压缩字符串,但通过参数设置可以对整数做压缩。

MyISAM 压缩每个索引块的方法是,先完全保存索引块中的第一个值,然后将其他值和第一个值进行比较得到相同前缀的字节数和剩余的不同后缀部分,把这部分存储起来即可。例如,索引块中的第一个值是 “perform”,第二个值是”performance“,那么第二个值的前缀压缩后存储的是类似”7.ance“这样的形式。MyISAM对行指针也采用类似的前缀压缩方式。

压缩块使用更少的空间,代价是某些操作可能更慢。因为每个值的压缩前缀都依赖前面的值,所以在查找时无法在索引块使用二分查找而只能从头开始扫描。正序扫描速度还不错,倒序就不好了。所有在块中查找某一行的操作平均都需要扫描半个索引块。

冗余和重复索引

MySQL 允许在相同列上创建多个索引,无论是有意的还是无意的。MySQL 需要单独维护重复的索引,并且优化器在优化查询的时候也需要逐个地进行考虑,这会影响性能。

重复索引是指在相同列上按照相同的顺序创建相同类型的索引。应该避免这样创建重复索引。

冗余索引和重复索引有一些不同。如果创建了索引(a,b),在创建索引(a)就是冗余索引,因为这只是一个前缀索引的前缀索引。不同类型的索引也不会是冗余索引。

未使用的索引

如果有永远不会使用的索引应该删除,会拖累性能。

索引和锁

索引可以让查询锁定更少的行。如果你的查询从不访问那些不需要的行,那么就会锁定更少的行,从两个方面来说这对性能都有好处。首先,虽然InnoDB的行锁效率很高,内存使用的也很少,但是锁定行的时候仍然会带来额外开销;其次,锁定超过需要的行会增加锁争用而较少并发性。

InnoDB 只有在访问行的时候才会对其加锁,而索引能够减少InnoDB访问的行数,从而减少锁的数量。但这只有当InnoDB 在存储引擎层能够过滤掉素有不需要的行时才有效。如果索引无法过滤到无效的行,那么在InnoDB 检索到数据并返回给服务器层以后,MySQL 服务器才能应用WHERE子句。这时候已经无法避免锁定行了。

关于 InnoDB 、索引和锁有一些很少有人知道的细节:InnoDB 在二级索引上使用共享锁,但访问主键索引需要排它锁。这消除了使用覆盖索引的可能性,并且使得 SELECT FOT UPDATE 比 LOCK IN SHARE MODE 或非锁定查询要慢很多。

暂无评论