MySQL 的存储引擎(Storage Engines)
MySQL 有两种存储引擎(Storage Engines):
- InnoDB’s
- MyISAM
InnoDB
is a general-purpose storage engine that balances high reliability and high performance. In MySQL 8.0, InnoDB
is the default MySQL storage engine. Unless you have configured a different default storage engine, issuing a CREATE TABLE
statement without an ENGINE
clause creates an InnoDB
table.
MyISAM
MyISAM 不支持聚簇索引,因此,MyISAM 中的主键索引(Primary Index) 和辅助索引(Secondary Index),都是非聚集索引(Non-clustered Index)。
在 MyISAM 的 B+Tree 的叶子节点上的 data 域中,并不是保存数据本身,而是保存数据存放在磁盘中的地址。
MyISAM 按照数据插入的顺序存储在磁盘上,如下图所示,左边为行号(row number),从 0 开始。因为元组的大小固定,所以 MyISAM 很容易的从表的开始位置找到某一特定行的位置。
MyISAM 中的主键索引(Primary Index) - 非聚集索引(Non-clustered Index)
MyISAM 建立的基于 col1 为主键的主键索引大致如下图所示,索引中每一个叶子节点仅仅包含行号(row number),且叶子节点按照 col1 的顺序存储。
因此,由于 MyISAM 中的主键索引(Primary Index)是非聚集索引(Non-clustered Index),因此数据表中数据存储顺序与索引顺序无关,而与数据的插入顺序有关。
我们换一种图的表达形式。在下图中,表一共有三列,假设以 col1 为主键,可以看出,MyISAM 的叶子节点中保存的实际上是指向存放数据的物理块的指针。从 MYISAM 存储的物理文件看出,MyISAM 引擎的索引文件(.MYI)和数据文件(.MYD)是相互独立的,索引文件仅仅保存数据记录的地址。
MyISAM 中的辅助索引(Secondary Index) - 非聚集索引(Non-clustered Index)
以对列 col2 进行索引为例。在 MyISAM 的辅助索引(Secondary Index)中,每一个叶子节点中仅仅包含行号(row number),且叶子节点按照 col2 值的大小顺序进行存储。
因此,由于 MyISAM 中的辅助索引(Secondary Index)是非聚集索引(Non-clustered Index),因此数据表的数据存储顺序也与索引顺序无关,
InnoDB
InnoDB 中的主键索引(Primary Index) - 聚集索引(Clustered Index)
下图展示了 InnoDB 中的主键索引(Primary Index)的记录是如何存放的。注意到,节点页只包含了索引值,叶子页包含行的全部数据。整个主键索引是一个 B+Tree 的数据结构:
- When you define a
PRIMARY KEY
on a table,InnoDB
uses it as the clustered index. A primary key should be defined for each table. If there is no logical unique and non-null column or set of columns to use a the primary key, add an auto-increment column. Auto-increment column values are unique and are added automatically as new rows are inserted. - If you do not define a
PRIMARY KEY
for a table,InnoDB
uses the firstUNIQUE
index with all key columns defined asNOT NULL
as the clustered index. - If a table has no
PRIMARY KEY
or suitableUNIQUE
index,InnoDB
generates a hidden clustered index namedGEN_CLUST_INDEX
on a synthetic column that contains row ID values. The rows are ordered by the row ID thatInnoDB
assigns. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in order of insertion.
因此,因为 InnoDB 的数据文件本身要按主键聚集,所以 InnoDB 要求表必须有主键(MyISAM 可以没有)。
在 InnoDB 中,表数据文件本身就是按 B+Tree 组织的一个索引结构,这棵树的叶子节点的 data 域保存了整张表的完整的数据记录。这个索引的 key 是数据表的主键,因此 InnoDB 表数据文件本身就是主索引。
上图展示了一个 InnoDB 表的索引。可以看到,InnoDB 索引的每一个叶子节点都包含了主键值、事务 ID、用于事务和 MVCC 的回流指针以及所有的剩余列(在这个例子中是 col2)。
InnoDB 中的辅助索引(Secondary Index) - 非聚集索引(Non-clustered Index)
Indexes other than the clustered index are known as secondary indexes. In InnoDB
, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. InnoDB
uses this primary key value to search for the row in the clustered index.
InnoDB 辅助索引的叶子节点中存储的不是” 行指针 “,而是主键值,并以此作为指向行的 “指针”。这样的策略减少了当出现行移动或者数据页分裂时辅助索引的维护工作。使用主键值当作指针会让辅助索引占用更多的空间,换来的好处是,InnoDB 在移动行时无须更新辅助索引中的这个 “指针”。
下图展示了基于 col2 的辅助索引。每一个叶子节点都包含了索引列(这里是 col2)和主键值(col1)。
InnoDB 与 MyIASM 索引和数据布局对比
下图对比 InnoDB 和 MyISAM 的主键索引与辅助索引:
可以看到,InnoDB 的的辅助索引的叶子节点存放的是 KEY 字段加主键值。因此,通过辅助索引查询首先查到是主键值,然后 InnoDB 再根据查到的主键值通过主键索引找到相应的数据块。
而 MyISAM 的辅助索引叶子节点存放的还是列值与行号的组合,叶子节点中保存的是数据的物理地址。所以可以看出 MYISAM 的主键索引和辅助索引没有任何区别,主键索引仅仅只是一个叫做 PRIMARY 的唯一、非空的索引,且 MYISAM 引擎中可以不设主键。
Reference
- 《High Performance MySQL》
- https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html
- https://dev.mysql.com/doc/refman/8.0/en/innodb-introduction.html
- https://www.xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/
- https://www.jianshu.com/p/54c6d5db4fe6