17.6.2.1 聚簇索引和辅助索引
每个InnoDB表都有一个特殊的索引,称为聚簇索引,用于存储行数据。通常情况下,聚簇索引与主键同义。为了在查询、插入和其他数据库操作中获得最佳性能,了解InnoDB如何使用聚簇索引优化常见的查找和数据操作语言(DML)操作非常重要。
在表上定义PRIMARY KEY
(主键)时,InnoDB会将其用作聚簇索引。每张表都应该定义一个主键。如果没有逻辑上唯一且非空的列或列集可作为主键,可以添加一个自增长列。自增长列的值是唯一的,并且在插入新行时会自动添加。
如果没有为表定义PRIMARY KEY
,InnoDB会将第一个所有键列都定义为NOT NULL
的UNIQUE
(唯一)索引用作聚簇索引 。
如果表没有PRIMARY KEY
或合适的UNIQUE
索引,InnoDB会在一个包含行ID值的合成列上生成一个名为GEN_CLUST_INDEX
的隐藏聚簇索引。行按照InnoDB分配的行ID进行排序。行ID是一个6字节的字段,随着新行的插入单调递增。因此,按行ID排序的行在物理上是按照插入顺序排列的。
聚簇索引如何加快查询速度
通过聚簇索引访问行的速度很快,因为索引搜索可以直接定位到包含行数据的页面。如果表很大,与那些将行数据和索引记录存储在不同页面的存储结构相比,聚簇索引结构通常可以节省一次磁盘I/O操作。
辅助索引与聚簇索引的关系
除聚簇索引外的其他索引被称为辅助索引。在InnoDB中,辅助索引中的每个记录都包含对应行的主键列,以及为辅助索引指定的列。InnoDB使用这个主键值在聚簇索引中搜索对应的行。
如果主键很长,辅助索引会占用更多空间,因此使用短主键更有优势。
有关利用InnoDB聚簇索引和辅助索引的指导原则,请参见10.3节 “优化和索引”。
17.6.2.2 InnoDB索引的物理结构
除空间索引外,InnoDB索引采用B树数据结构。空间索引使用R树,这是一种专门用于为多维数据建立索引的数据结构。索引记录存储在其B树或R树数据结构的叶子页中。索引页的默认大小为16KB,该页大小在MySQL实例初始化时由innodb_page_size
设置决定,详见17.8.1节 “InnoDB启动配置”。
当新记录插入到InnoDB聚簇索引时,InnoDB会尝试预留页面空间的1/16,以便未来对索引记录进行插入和更新操作。如果索引记录按顺序(升序或降序)插入,生成的索引页大约会占用15/16的空间;如果记录随机插入,页面占用空间在1/2到15/16之间。
InnoDB在创建或重建B树索引时会执行批量加载操作,这种索引创建方法也称为排序索引构建。空间索引不支持排序索引构建。innodb_fill_factor
变量用于定义在排序索引构建过程中每个B树页面填充的空间百分比,剩余空间则预留用于未来的索引增长。有关排序索引构建的更多信息,详见17.6.2.3节 “排序索引构建”。将innodb_fill_factor
设置为100时,会为聚簇索引页预留1/16的空间用于未来的索引增长。
如果InnoDB索引页的填充因子低于MERGE_THRESHOLD
(未指定时默认为50%),InnoDB会尝试收缩索引树以释放该页面。MERGE_THRESHOLD
设置适用于B树和R树索引。更多信息,详见17.8.11节 “配置索引页的合并阈值”。
17.6.2.3 排序索引构建
InnoDB在创建或重建索引时,采用批量加载的方式,而非逐行插入索引记录。这种索引创建方法也被称为排序索引构建。不过,空间索引不支持排序索引构建。
索引构建分为三个阶段。在第一阶段,会扫描聚簇索引,生成索引条目并添加到排序缓冲区。当排序缓冲区满时,会对条目进行排序并写入临时中间文件。这个过程也被称为一次“运行”。在第二阶段,针对已写入临时中间文件的一次或多次“运行”的结果,对文件中的所有条目执行归并排序。在第三阶段,也就是最后阶段,将已排序的条目插入到B树中。
在引入排序索引构建之前,索引条目是通过插入API逐行插入到B树中的。这种方法需要打开B树游标以查找插入位置,然后使用乐观插入法将条目插入到B树页面。如果由于页面已满导致插入失败,则会执行悲观插入,即打开B树游标,并根据需要拆分和合并B树节点,以便为条目找到空间。这种“自顶向下”构建索引的方法的缺点在于,查找插入位置的成本较高,且需要不断地拆分和合并B树节点。
排序索引构建采用“自底向上”的方法来构建索引。通过这种方法,在B树的所有层级中,都持有对最右侧叶子节点页面的引用。会分配B树所需深度的最右侧叶子节点页面,并按照排序顺序插入条目。一旦叶子节点页面已满,就会在父页面中追加一个节点指针,并分配一个兄弟叶子节点页面用于下一次插入。这个过程会持续进行,直到插入所有条目,这可能会导致插入操作一直进行到根节点层级。当分配兄弟页面时,会释放对之前固定的叶子节点页面的引用,新分配的叶子节点页面将成为最右侧叶子节点页面和新的默认插入位置。
为未来索引增长预留B树页面空间
为了给未来的索引增长留出空间,可以使用innodb_fill_factor
变量来预留B树页面空间的百分比。例如,将innodb_fill_factor
设置为80,会在排序索引构建过程中,为B树页面预留20%的空间。该设置适用于B树的叶子节点页面和非叶子节点页面,但不适用于存储TEXT
或BLOB
条目的外部页面。实际预留的空间量可能与配置不完全一致,因为innodb_fill_factor
的值被视为一种提示,而非严格限制。
排序索引构建与全文索引支持
排序索引构建支持全文索引。以前,是使用SQL将条目插入到全文索引中。
排序索引构建与压缩表
对于压缩表,之前的索引创建方法是将条目同时追加到压缩页面和未压缩页面。当表示压缩页面空闲空间的修改日志已满时,会对压缩页面进行重新压缩。如果由于空间不足导致压缩失败,则会拆分页面。而在排序索引构建中,条目仅追加到未压缩页面。当未压缩页面已满时,会对其进行压缩。自适应填充用于确保在大多数情况下压缩成功,但如果压缩失败,会拆分页面并再次尝试压缩。这个过程会持续进行,直到压缩成功。有关B树页面压缩的更多信息,请参见17.9.1.5节 “InnoDB表的压缩原理”。
排序索引构建与重做日志记录
在排序索引构建期间,重做日志记录是禁用的。取而代之的是设置检查点,以确保索引构建能够承受意外退出或失败的情况。检查点会强制将所有脏页写入磁盘。在排序索引构建过程中,会定期向页面清理线程发出信号,以将脏页刷新到磁盘,确保检查点操作能够快速处理。通常情况下,当干净页面数量低于设定阈值时,页面清理线程会刷新脏页。而对于排序索引构建,会及时刷新脏页,以减少检查点开销,并使I/O和CPU活动并行化。
排序索引构建与优化器统计信息
排序索引构建生成的优化器统计信息,可能与之前的索引创建方法生成的统计信息有所不同。统计信息的差异是由于填充索引所使用的算法不同导致的,但预计这种差异不会影响工作负载的性能。