表的统计数据:SHOW TABLE STATUS LIKE 'table_name';
索引的统计数据:SHOW INDEX FROM table_name;
13.1 两种不同的统计数据存储方式
InnoDB 提供了两种存储统计数据的方式:
- 永久性的统计数据。存储在磁盘上,服务器重启之后还在。
- 非永久性的统计数据。存储在内存中,随服务器关闭而清除,服务器重启时可以重新收集。
13.2 基于磁盘的永久性统计数据
当我们选择把某个表以及该表索引的统计数据存放到磁盘上时,实际上是把这些统计数据存储到了两个表里:
SHOW TABLES FROM mysql LIKE 'innodb%';
- innodb_table_stats:存储了关于表的统计数据,每一条记录对应着一个表的统计数据。
- 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
- 从数据字典里找到表的各个索引对应的根页面位置
- 从根页面的 Page Header 里找到叶子节点段和非叶子节点段对应的 Segemnt Header
- 从叶子节点段和非叶子节点段的 Segemnt Header 中找到这两个段对应的 INODE Entry 结构
- 从对应的 INODE Entry 结构中找到该段对应所有零散的页面地址以及 FREE、NOT_FULL 和 FULL 链表的基节点
- 直接统计零散的页面有多少个,然后从那三个链表的 List Length 字段中读出该段占用的区的大小,每个区占用64个页,所以就可以统计出整个段占用的页面
- 分别计算聚簇索引和其余索引
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 定期更新统计数据
- 开启 innodb_stat_auto_recalc
- 手动调用 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 值,有三个候选值:
- nulls_equals:认为所有 NULL 值都是相等的。默认值
- nuls_unequals:认为所有 NULL 都是不相等的。
- nulls_ignored:直接把 NULL 值忽略掉。
13.5 总结
InnoDB 以表为单位来收集统计数据,可以是基于磁盘的永久性数据,也可以是基于内存的非永久性数据