MySQL学习笔记9

22 阅读4分钟

聚簇索引

InnoDB的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。当表有聚簇索引时,它的数据行实际上存放在索引的叶子页(leaf page)中。Inno DB只能通过主键实现聚簇索引(不能像其它db那样选择某个索引作为聚簇索引)。

聚簇索引的优点:

  • 可以把相关数据保存在一起。
  • 数据访问更快。
  • 使用覆盖索引扫描的查询可以直接使用页节点中的主键值。

聚簇索引的缺点:

  • 插入速度严重依赖于插入顺序。按照主键的顺序插入是加载数据到InnoDB表中速度最快的方式。
  • 更新聚簇索引列的代价很高,因为会强制InnoDB将每个被更新的行移动到新的位置。
  • 基于聚簇索引的表在插入新行,或者主键被更新导致需要移动行的时候,可能面临“页分裂(page split)”的问题。
  • 二级索引(非聚簇索引)可能比想象的要更大,因为在二级索引的叶子节点包含了引用行的主键列
  • 二级索引访问需要两次索引查找,而不是一次。

二级索引叶子节点保存的不是指向行的物理位置的指针,而是行的主键值

这意味着通过二级索引查找行,存储引擎需要找到二级索引的叶子节点获得对应的主键值,然后根据这个值去聚簇索引中查找到对应的行。这里做了重复的工作:两次B-Tree查找而不是一次。(回表查询)

最好避免随机的(不连续且值的分布范围非常大)聚簇索引。

从性能的角度考虑,使用UUID来作为聚簇索引则会很糟糕:它使得聚簇索引的插入变得完全随机。

覆盖索引

MySQL也可以使用索引来直接获取列的数据,这样就不再需要读取数据行。如果索引的叶子节点中已经包含要查询的数据,那么还有什么必要再回表查询呢?如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。

覆盖索引好处:

索引条目通常远小于数据行大小。

因为索引是按照列值顺序存储的(至少在单个页内是如此),所以对于I/O密集型的范围查询会比随机从磁盘读取每一行数据的I/O要少得多。

二级索引在叶子节点中保存了行的主键值,所以如果二级主键(个人理解是主键加上二级索引列)能够覆盖查询,则可以避免对主键索引的二次查询。

MySQL查询优化器会在执行查询前判断是否有一个索引能进行覆盖,如果条件为假(false),MySQL 5.5和更早的版本也总是会回表获取数据行。(条件为false的一个典型例子:索引覆盖了WHERE条件中的字段,但不是整个查询涉及的字段)

一个无法覆盖查询的例子:

SELECT * FROM products WHERE actor='SEAN CARREY'
        -> AND title like '%APOLLO%'\G

这里索引无法覆盖该查询,有两个原因:

没有任何索引能够覆盖这个查询。因为查询从表中选择了所有的列,而没有任何索引覆盖了所有的列。

MySQL不能在索引中执行LIKE操作。MySQL能在索引中做最左前缀匹配的LIKE比较,,但是如果是通配符开头的LIKE查询,存储引擎就无法做比较匹配。

InnoDB的二级索引的叶子节点都包含了主键的值,这意味着InnoDB的二级索引可以有效地利用这些“额外”的主键列来覆盖查询。

sakila.actor使用InnoDB存储引擎,并在last_name字段有二级索引,虽然该索引的列不包括主键actor_id,但也能够用于对actor_id做覆盖查询:

SELECT actor_id, last_name
        -> FROM sakila.actor WHERE last_name = 'HOPPER'\G