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