第 13 章 兵马未动,粮草先行——InnoDB 统计数据是如何收集的

18 阅读3分钟

表的统计数据:SHOW TABLE STATUS LIKE 'table_name';

索引的统计数据:SHOW INDEX FROM table_name;

13.1 两种不同的统计数据存储方式

InnoDB 提供了两种存储统计数据的方式:

  1. 永久性的统计数据。存储在磁盘上,服务器重启之后还在。
  2. 非永久性的统计数据。存储在内存中,随服务器关闭而清除,服务器重启时可以重新收集。

13.2 基于磁盘的永久性统计数据

当我们选择把某个表以及该表索引的统计数据存放到磁盘上时,实际上是把这些统计数据存储到了两个表里:

SHOW TABLES FROM mysql LIKE 'innodb%';

在这里插入图片描述

  1. innodb_table_stats:存储了关于表的统计数据,每一条记录对应着一个表的统计数据。
  2. innodb_index_stats:存储了关于索引的统计数据,每一条记录对应着一个索引的一个统计项的统计数据。

13.2.1 innodb_table_stats

SELECT * FROM mysql.innodb_table_stats;

在这里插入图片描述

字段名描述
database_name库名
table_name表名
last_update本条记录最后更新时间
n_rows表中记录数(估计值)
clustered_index_size表的聚簇索引占用的页面数量(估计值)
sum_of_other_index_sizes表的其他索引占用的页面数量(估计值)
13.2.1.1 n_rows 统计项的收集

按照一定算法(并不是纯粹随机)选取几个叶子节点页面,计算每个页面中主键值的记录数量,计算平均数后×全部叶子节点数量 = n_rows,所以是一个估计值。

13.2.1.2 clustered_index_size 和 sum_of_other_index_sizes
  1. 从数据字典里找到表的各个索引对应的根页面位置
  2. 从根页面的 Page Header 里找到叶子节点段和非叶子节点段对应的 Segemnt Header
  3. 从叶子节点段和非叶子节点段的 Segemnt Header 中找到这两个段对应的 INODE Entry 结构
  4. 从对应的 INODE Entry 结构中找到该段对应所有零散的页面地址以及 FREE、NOT_FULL 和 FULL 链表的基节点
  5. 直接统计零散的页面有多少个,然后从那三个链表的 List Length 字段中读出该段占用的区的大小,每个区占用64个页,所以就可以统计出整个段占用的页面
  6. 分别计算聚簇索引和其余索引

13.2.2 innodb_index_stats

SELECT * FROM mysql.innodb_index_stats WHERE table_name = 'single_table';

在这里插入图片描述

字段名描述
database_name库名
table_name表名
index_name索引名
last_udpate本条记录最后更新时间
stat_name统计项的名称
stat_value统计项的值
sample_size为生成统计数据而采样的页面数量
stat_description统计项的描述
统计项描述
n_leaf_pages索引的叶子节点占用多少页面
size索引共占用多少页面
n_diff_pfxNN索引列不重复的值有多少

13.2.3 定期更新统计数据

  1. 开启 innodb_stat_auto_recalc
  2. 手动调用 ANALYZE TABLE 语句

13.2.4 手动更新 innodb_table_stats 和 innodb_index_stats 表

UPDATE innodb_table_stats
SET n_rows = 1
WHERE table_name = 'single_table';
FLUSH TABLE single_table;

13.3 基于内存的非永久性统计数据

新版本 MySQL 不用

13.4 innodb_stats_method 的使用

计算某个索引列不重复值的数量时如何对待 NULL 值,有三个候选值:

  1. nulls_equals:认为所有 NULL 值都是相等的。默认值
  2. nuls_unequals:认为所有 NULL 都是不相等的。
  3. nulls_ignored:直接把 NULL 值忽略掉。

13.5 总结

InnoDB 以表为单位来收集统计数据,可以是基于磁盘的永久性数据,也可以是基于内存的非永久性数据