第13期:表统计信息的计算

本文深入探讨MySQL中的表和索引统计信息的计算方法,包括持久化与非持久化统计信息的区别,以及如何手动更新统计信息等内容。

本篇介绍 MySQL 表如何计算统计信息。表统计信息是数据库基于成本的优化器最重要的参考信息;统计信息不准确,优化器可能给出不够优化的执行计划或者是错误的执行计划。

对统计信息的计算分为 非持久化统计信息(实时计算)与 持久化统计信息

非持久化统计信息
  • 统计信息没有保存在磁盘上,而是频繁的实时计算统计信息;
  • 每次对表的访问都会重新计算其统计信息;

假设针对一张大表的频繁查询,那么每次都要重新计算统计信息,很耗费资源。

持久化统计信息
  • 把一张表在某一时刻的统计信息值保存在磁盘上;
  • 避免每次查询时重新计算;
  • 如果表更新不是很频繁,或者没有达到 MySQL 必须重新计算统计信息的临界值,可直接从磁盘上获取;
  • 即使 MySQL 服务重启,也可以快速的获取统计信息值;
  • 统计信息的持久化可以针对全局设置也可以针对单表设置。

接下来,详细说 MySQL 统计信息如何计算,何时计算,效果评估等问题。

在 MySQL Server 层来控制是否自动计算统计信息的分布,并且来决策是持久化还是非持久化。

一、持久化统计相关参数:

  • innodb_stats_persistent :是否开启统计信息持久化,默认开启。
  • innodb_stats_auto_recalc :是否自动重新计算持久化统计信息,默认开启。

二、具体的更新策略为:

当一张表数据变化超过 10% 后,MySQL 会针对这张表统计信息的更新时间戳做一个判断,检查最后一次更新的时间是否超过 10 秒; 如果不到 10 秒,把这张表加到一个统计信息更新队列中,到时间了再重新计算;如果超过了10秒,直接重新计算,并且更新时间戳。

目前这个超时时间写死在 MySQL 代码里,暂时不能更改。不过在某些 MySQL 分支版还可以控制这个时间,比如 Percona。

  • innodb_stats_include_delete_marked : 更新持久化统计信息时,是否会计算已经标记为删除的行。

    默认是关闭的,会获取未提交的脏数据。开启这个选项,MySQL计算统计信息时只会考虑已经提交的数据。

  • innodb_stats_persistent_sample_pages : 用于更新持久化索引分布或者其他统计信息的随机基数页,默认20个。

    页数越多,统计信息也就越准确,也就有助于查询优化器选择最优的查询计划。

什么时候考虑更改这个值呢?
  1. 当查询计划不是很准确时。 比如对比指定表在系统表mysql.innodb_index_stats 的数据跟 distinct 查询的结果,如果相差太大,可以考虑增加这个值。
  2. 当 analyze table 变的非常慢时,可能是这个值设置的太大了,此时要考虑减小这个值。

三、非持久化统计信息参数

  • innodb_stats_transient_sample_pages:设置非持久化统计信息的采样页数目,默认8个。

  • innodb_stats_on_metadata:当统计信息配置为非持久化时生效,默认关闭。

    参数 innodb_stats_persistent 为 0 或者建表时属性STATS_PERSISTENT=0 才起作用。

当开启后,对以下元数据的访问会自动更新统计信息:
  • show table status
  • show index
  • information_schema.tables
  • information_schema.statistics

所以开启这个选项会额外增加访问表的开销,特别是大表。

还有一些其他的场景会自动更新非持久化统计信息,比如:
  1. 表第一次被访问;
  2. InnoDB 检测到有十六分之一的表自从上次统计信息计算后被更新了,这时触发自动更新;
  3. MySQL 客户端默认选项 --auto-rehash 打开所有 InnoDB 表,导致所有 InnoDB 表被自动更新统计信息;

四、表属性控制

STATS_AUTO_RECALC

用来指定是否要自动计算指定 InnoDB 表的统计信息。

三个值:default, 0, 1

  • default:也就是默认值,依赖 server 端参数 innodb_stats_auto_recalc 的设置效果
  • 0:表示禁用统计信息的自动重新计算,也就是永远不重新计算,需要手动执行 analyze table
  • 1:表示当表数据有 10% 的数据变化后,则重新计算持久化统计信息。
STATS_PERSISTENT

用来指定是否要开启指定 InnoDB 表的统计信息持久化。

三个值:default, 0, 1

  • default:依赖 server 端参数 innodb_stats_persistent 的设置
  • 0:表示不需要持久化统计信息
  • 1:表示开启持久化统计信息
STATS_SAMPLE_PAGES

用来指定计算统计信息时的采样页数量。

五、手动更新统计信息

analyze table 用来手动更新表统计信息。建议在业务低峰时执行。

六、持久化表统计元数据信息

优化器通过两张元数据表里的数据来决定查询最优执行计划。

表统计信息保存在表 mysql.innodb_table_stats 里

比如表 ytt_sample_persist 的统计信息

重要列说明:

  • n_rows:表的行数
  • clustered_index_size:主键的数据页个数
  • sum_of_other_index_sizes:二级索引的数据页个数

以下例子可以看到表 ytt_sample_persist 表行数大概为 36W 行,聚簇索引页数为 15162,二级索引页数为 4113。

这些值都是基于采样页来计算的,所以是一个预估值。

mysql> select n_rows,clustered_index_size,sum_of_other_index_sizes  from innodb_table_stats where database_name ='ytt' and table_name = 'ytt_sample_persist';
+--------+----------------------+--------------------------+
| n_rows | clustered_index_size | sum_of_other_index_sizes |
+--------+----------------------+--------------------------+
| 356960 |                15162 |                     4113 |
+--------+----------------------+--------------------------+
1 row in set (0.00 sec)

其实表 ytt_sample_persist 真实数据为 40W 行。

mysql> select count(*) from ytt_sample_persist;
+----------+
| count(*) |
+----------+
|   406644 |
+----------+
1 row in set (0.90 sec)

强制更新统计信息

mysql> analyze table ytt_sample_persist;
+------------------------+---------+----------+----------+
| Table                  | Op      | Msg_type | Msg_text |
+------------------------+---------+----------+----------+
| ytt.ytt_sample_persist | analyze | status   | OK       |
+------------------------+---------+----------+----------+
1 row in set (0.28 sec)

统计信息已经更新为最新,数据又离真实数据接近一点。

mysql> select n_rows,clustered_index_size,sum_of_other_index_sizes from innodb_table_stats where database_name ='ytt' and table_name ='ytt_sample_persist';
+--------+----------------------+--------------------------+
| n_rows | clustered_index_size | sum_of_other_index_sizes |
+--------+----------------------+--------------------------+
| 387202 |                16380 |                     4562 |
+--------+----------------------+--------------------------+
1 row in set (0.01 sec)
索引统计信息保存在表 mysql.innodb_index_stats 里

比如表 ytt_sample_persist 索引统计信息

  • Index_name : 索引名字
  • stat_name / stat_value:统计名字和对应的值
  • sample_size: 采样页个数
  • stat_description:统计名字详细信息描述

比如表 ytt_sample_persist 的联合主键统计信息如下:

stat_name 显示的值 n_diff_pfx01 代表联合主键中第一个列状态名字,对应的 stat_value 为第一个列的唯一值个数;n_diff_pfx02 代表第二列状态名字,对应的 stat_value 为前两列的唯一值个数,以此类推。

n_leaf_pages /stat_value 代表叶子节点的页数目;size 代表索引的总页数。

mysql> select index_name,stat_name,stat_value,sample_size,stat_description from innodb_index_stats where database_name ='ytt' and table_name ='ytt_sample_persist'  and index_name = 'PRIMARY';
+------------+--------------+------------+-------------+-----------------------------------+
| index_name | stat_name    | stat_value | sample_size | stat_description                  |
+------------+--------------+------------+-------------+-----------------------------------+
| PRIMARY    | n_diff_pfx01 |      14137 |          20 | i1                                |
| PRIMARY    | n_diff_pfx02 |      75398 |          20 | i1,i2                             |
| PRIMARY    | n_diff_pfx03 |     387202 |          20 | i1,i2,i3                          |
| PRIMARY    | n_leaf_pages |      15708 |        NULL | Number of leaf pages in the index |
| PRIMARY    | size         |      16380 |        NULL | Number of pages in the index      |
+------------+--------------+------------+-------------+-----------------------------------+
5 rows in set (0.00 sec)

那关于表的统计信息相关知识点就介绍到此,了解这块对我们优化 SQL 来说,会更加得心应手。

总结

简单总结下,本篇主要介绍了 MySQL 表和索引的统计信息计算,包括持久化统计信息与非持久化统计信息。如果后期有 SQL 走的执行计划不对,或者不是最优的,那就可以断定相关统计信息太旧了,需要及时更新。比如有时候多表 JOIN 的顺序不对,导致查询效率变差,需要人工介入等等。


关于 MySQL 的技术内容,你们还有什么想知道的吗?赶紧留言告诉小编吧!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值