MySQL——高性能索引策略

时间:2022-05-16 20:08:44

1.隔离列

如果在查询中没有隔离索引的列,MySQL通常不会使用索引。“隔离”意味着它不是表达式的一部分,也没有位于函数中。


2.前缀索引和索引的选择性

有时需要索引很长的字符列,它会使索引变大并且变慢。一个策略就是模拟哈希索引。但是有时这也不够好,那么该怎么办?

通常可以索引开始的几个字符,而不是全部值,以节约空间并得到好的性能。这使索引需要的空间变小,但是也会降低选择性。索引选择性是不重复的索引值和表中所有行的比值。高选择性的索引有好处,因为它使MySQL在查找匹配的时候可以过滤更多的行。唯一索引的选择率为1,为最佳值 。

矛盾在于选择足够长的前缀会得到好的选择性,但是短的前缀会节约空间。前缀应该足够长,使它的选择性能够接近于索引整个列。换句话说,前缀的基数性应该接近于全列的基数性。

前缀索引能很好地减少索引的大小及提高速度,但是它也有坏处:MySQL不能在ORDER BY或GROUP BY查询中使用前缀索引,也不能把它们用作覆盖索引。


3. 聚集索引

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

当表有聚集索引的时候,它的数据行实际保存在索引的叶子页中。术语“聚集”指实际的数据行和相关的键值都保存在一起。每个表只能有一个聚集索引,因为不能一次把行保存在两个地方。

由于是存储引擎负责实现索引,因此不是所有的存储引擎都支持聚集索引。当前,SolidDB和InnoDB是唯一支持聚集索引的存储引擎。

一些数据库服务器可以选择聚集的列,但是目前没有任何MySQL的存储引擎能做到这点。InnoDB按照主键进行聚集。

如果没有定义主键,InnoDB会试着使用唯一的非空索引来代替。如果没有这种索引,InnoDB就会定义隐藏的主键然后再上面进行聚集。InnoDB只能聚集在同一页面中的记录。包含相邻键值的页面也许会相距甚远。

聚集的数据有以下优点:

可以把相关数据保存在一起。

数据访问快。聚集索引把索引和数据都保存在了同一棵B-Tree树中。

使用覆盖索引的查询可以使用包含在叶子节点中的主键值。

缺点:

聚集能最大限度地提升I/O密集负载的性能。如果数据能装入内存,那么其顺序也就无所谓了,这样聚集就没什么用处。

插入速度严重依赖于插入顺序。按照主键的顺序插入行是把数据装入InnoDB表最快的方法。如果没有按照主键顺序插入数据,那么在插入之后最好使用OPTIMIZE TABLE重新组织一下表。

更新聚集索引列是昂贵的,因为它强制InnoDB把每个更新的行移动到新的位置。

建立在聚集索引上的表在插入新行,或者在行的主键被更新,该行必须被移动的时候会进行分页。分页发生在行的键值要求行必须被放在一个已经放满了数据的也得时候,此时存储引擎必须分页才能容纳该行。分页会导致表占用更多的磁盘空间。

聚集表可能会比全表扫描慢,尤其在表存储得比较稀疏或因为分页而没有顺序存储的时候。

第二(非聚集)索引可能会比预期的大,因为它们的叶子节点包含了被引用行的主键列。

第二索引访问需要两次索引查找,而不是一次。

最后一点有点迷惑人。答案在于第二索引保存的“行指针”的本质。叶子节点不会保存引用的行的物理位置,而是保持了行的主键值。

这意味着为了从第二索引查找行,存储引擎首先要找到叶子节点,然后使用保存在那里的主键值找到主键,最终找到行。这需要两次动作,两次B-Tree导航,而不是一次。(在InnoDB中,自适应哈希索引能减少这种损失。)

比较InnoDB和MyISAM的数据布局: InnoDB支持聚集索引,因此它以不同于MyISAM的方式保存数据。

4. 覆盖索引

包含所有满足查询需要的数据的索引叫覆盖索引。

索引记录通常远小于全行大小,因此,如果只读索引,MySQL就能极大地减少数据访问量。因为索引比数据小很多,能更好地被装入内存。(这对于MyISAM尤其正确,它能压缩索引,使它们变得更小。)

索引时按照索引值进行排序的,因此I/O密集型范围访问将会比随机地从磁盘上提取每一行数据要快的多。

大部分存储引擎缓存索引比缓存数据更好。一些存储引擎,例如MyISAM,只在MySQL内存中缓存了索引。因为操作系统缓存了给MyISAM的数据,访问它通常需要系统调用。这也许会导致巨大的性能影响,尤其对于那些系统调用占了数据访问中最大开销的负载。

覆盖索引对于InnoDB表特别有用,因为InnoDB的聚集缓存。InnoDB的第二索引在叶子节点中保存了行的主键值。因此,覆盖了查询的第二索引在主键中避免了另外一次索引查找。

覆盖索引和任何索引都不一样。覆盖索引必须保存它包含的列的数据。哈希、空间和全文索引不会保存这些值,因此MySQL只能使用B-Tree索引来覆盖查询。并且,不同的存储引擎实现覆盖索引的方式不一样,不是所有的存储引擎都支持覆盖索引。

5 为排序使用索引扫描

MySQL有两种产生排序结果的方式:使用文件排序,或者扫描有序的索引。MySQL能为排序和查找行使用同样的索引。

按照索引对结果进行排序,只有当索引的顺序和ORDER BY子句中的顺序完全一致,并且所有列排序的方向(升序或降序)一样才可以。如果查询连接了多个表,只有在ORDER BY子句的所有列引用的是第一个表才可以。查找查询中的ORDER BY子句也有同样的局限:它要使用索引的最左前缀。在其他所有情况下,MySQL使用文件排序。

按照索引进行排序的一耳光最重要的用途是有ORDER BY和LIMIT子句的查询。


6. 压缩(前缀压缩)索引

MyISAM使用前缀压缩以减少索引大小,运行更多索引被装入内存,以及在某些情况下极大地提高性能。它在默认情况下会压缩字符串,但是可以让它压缩整数。

压缩后的块占用的空间较小,但是某些操作变慢了。因为每个值的压缩前缀依赖于前面的值,MyISAM不能再索引块中中庸二分查找找到想要的值,必须从头开始。顺序向前的操作性能尚可,但是反向扫描——例如ORDER BY DESC———不会很好地工作。任何须查找数据块中部的行的操作要对块进行扫描,平均说来,要扫描半个块。测试表明压缩后的键使MyISAM表上的索引查找对CPU密集的负载会慢几倍,因为扫描须随机查找。

权衡压缩的标准时CPU内存资源和磁盘资源的折中。


7. 多余和重复索引

选择扩展索引


8.索引和锁定

索引对InnoDB有很重要的作用,因为它会让查询锁定更少的行。

这里有一个很少人知道的关于InnoDB、索引和锁定的细节:InnoDB能在第二索引上放置共享(读取)锁,但是独占(写入)锁要求访问主键。这消除了使用覆盖索引的可能性,并且能导致SELECT FOR UPDATE比LOCK IN SHARE MODE或非锁定查询慢得多。