你不知道的B+树索引

2022-06-20 20:09:20 浏览数 (1)

平衡二叉树是搜索效率是非常高的, 其时间复杂度是O(log(N)).

B 树索引就是在平衡二叉树基础上发展而来的.

数据页

在介绍索引之前需要先介绍下数据页,

MySQL是按数据页读取数据的, 数据页是Innodb磁盘管理的最小单位.

其结构三部分:

(1)页头: 页 ID, 指向前一页和后一页的指针, 存储的数据类型等信息, 共 38B;

(2)根据不同的数据类型, 存储不同结构的数据, 包括数据页, Undo页, 系统页, 事务数据页等;

(3)页尾: 8B

默认数据页大小是16K(16384B), 可以根据参数 innodb_page_size配置.

每个数据页会使用一个32位的int值来表示, 正好对应Innodb最大64TB的存储容量(16K * 2^32=64T)

代码语言:javascript复制
mysql> show global variables = 'innodb_page_size'; -------------------------------------- ----------- | Variable_name                        | Value     | -------------------------------------- ----------- | innodb_page_size                     | 16384     | -------------------------------------- ----------- 

B 树索引

B 树索引的节点信息也是按页存储的, 一页存储一个节点数据, 非叶子节点存储数据的范围信息, 叶子节点存储指针和数据信息(不同引擎存储的信息也不同).

MyISAM索引文件和数据文件是分离的,叶子节点仅保存数据行记录的地址(行指针).

在Innodb引擎中,分为聚集索引(主键索引)和二级索引两种。

主键索引的叶节点中保存的是索引列值和完整的数据记录(整行数据);

二级索引的叶子节点保存的是索引列值和指向主键的指针;

节点信息是以任意顺序存储在磁盘上的, 其物理位置与索引顺序的并没有对应关系. 所以读取多页数据时, 是会产生随机 IO 的; 一般来说, 磁盘每秒差不多在100次IO左右,2-3次意味着查询时间只需0.02-0.03秒;

索引查找

了解了索引的基本结构, 再看下索引是如何查找数据的.

索引查找过程是先遍历根节点信息, 找到数据的范围信息对应子节点, 一层层遍历, 最后遍历到叶子节点, 找到对应数据.

以查找数据 8 为例, 整个过程如下:

每次查找子节点都是随机IO, 可见节点存储的信息越多越好, B 树的树高也越矮越好;

这都要求索引列越小越好.

索引构建

Innodb在创建或重建索引时,是批量加载导入数据的,而不是一次插入一个索引记录的。

索引创建分为三个阶段。

1.扫描聚簇索引,生成索引数据并将添加到排序缓冲区。当排序缓冲区已满时,会将数据进行排序并写到临文件中。

2.对所有数据进行合并排序。

3.将已排序的数据插入 B 树中。在写入数据时, 页会根据配置(参数: innodb_fill_factor)保留部分空间, 为以后的数据更新和插入时使用, 减少索引调整的几率.

查询命令:

代码语言:javascript复制
show variables where variable_name = 'innodb_fill_factor'

索引维护

1. 索引添加数据

当索引写入新数据时, 如果页空间已经达到阈值时(根据参数innodb_fill_factor配置), 就需要重新申请一个页空间, 并将原页空间中一半的数据复制过来, 整个过程被称为页分裂.

页分裂会降低空间的利用率, 造成空间的浪费.

但如果是索引树的尾部节点, 数据页写满时会新申请一个数据页, 就不会产生页分裂的问题, 这也是主键索引选择有序自增类型的原因.

2. 索引数据删除

既然有添加, 那就会有删除操作. 当数据被delete时, 页空间也就会被释放出来了. 当达到一定阈值(配置参数MERGE_THRESHOLD)时, 就会尝试和相邻页进行合并处理, 整个过程称之为页合并.

合并阈值参数的配置方式有两种

1.表级配置

代码语言:javascript复制
CREATE TABLE t1 ( id INT, KEY id_index (id) COMMENT 'MERGE_THRESHOLD=40' );

2.索引级配置

代码语言:javascript复制
ALTER TABLE t1 ADD KEY id_index (id) COMMENT 'MERGE_THRESHOLD=40';

降低MERGE_THRESHOLD值时, 能较少的页面合并尝试次数和页面合并次数, 但设置太小可能会导致大量数据文件, 因为空页空间过多.

索引的树高

B 树的高度, 直接影响到一次查找随机 IO 的次数和索引的使用效率.

下面一起看下树高的估算.

以bigint(占 8B 空间)型主键索引为例, 指针信息占 6B, 非叶子节点可以存 16384/(8 6) = 1170个对象.

如果一行数据是 1K 大小, 那叶子节点能存储 16 条数据.

再强调下, 主键索引的叶子节点存储的是一行数据.

如此算来:

树高为2的B 树:1170 * 16 = 18720,约存1.8w条数据记录;

树高为3的B 树:1170 * 1170 * 16 = 21902400,约存2千万条数据记录。

已经创建好的索引可以通过数据字典, 查询到包括树高在内的索引信息:

代码语言:javascript复制
select       a.space as tbl_spaceid,       a.table_id,       a.name as table_name,       file_format,       row_format,       space_type,       b.index_id,       b.name as index_name,       page_no,       b.type as index_type from       information_schema.innodb_sys_tables aleft join information_schema.innodb_sys_indexes b on a.table_id = b.table_id where       a.table_id = b.table_id and a.space != 0;

通过这篇文章是不发现了 B 树索引的另一面了.

0 人点赞