MySQL ANALYZE TABLE

2023年 11月 18日 23.8k 0

ANALYZE TABLE

ANALYZE TABLE 作用

  • ANALYZE TABLE 会统计索引分布信息,并将结果持久化存储;

  • 对于 MyISAM 表,相当于执行了一次 myisamchk –analyze;

  • 支持 InnoDB、NDB、MyISAM 等存储引擎,但不支持 视图(view);

  • ANALYZE TABLE也可以用在表分区上;

  • 对InnoDB、MyISAM表执行 ANALYZE TABLE 时,会加上读锁(read lock);

  • 执行 ANALYZE TABLE 会记录binlog。(这是合理的,因为索引分析这个操作,在MASTER端执行完后,SLAVE端也是需要的)

 

索引分布分析都干了啥

若自从上次索引分析后没有数据更新的话,执行 ANALYZE TABLE 并不会再分析一次。
optimizer 会根据索引分析结果来判断表 JOIN 的驱动顺序,以及选用哪个索引。

 

关于 innodb_stats_persistent 选项

我们可以通过设定该选项,决定索引分析结果是否要持久化存储到磁盘中。

不持久化存储的话,可能需要频繁更新统计信息,并由此引发执行计划反复变化。

这个设置在每个表创建(或后期 ALTER 修改)时都可以自行指定 STATS_PERSISTENT 选项,也可以设置全局选项 innodb_stats_persistent(这个选项设置为 1 时,则表统计信息将持久化存储)。

 

关于 innodb_stats_persistent_sample_pages 选项

该选项决定了每次统计索引及其他信息时要采集多少个data page,默认值是 20。

增加这个值,可以提高统计信息的精确度,同样也能提高执行计划的准确性,不过也相应增加了在InnoDB表上分析的I/O开销。

备注

  • 增加 innodb_stats_persistent_sample_pages 的值可能导致 ANALYZE TABLE 的耗时增加。可以参考下方公式估算执行 ANALYZE TABLE 的代价。

  • 只有在 innodb_stats_persistent 选项启用后,innodb_stats_persistent_sample_pages 也才能跟着生效,否则的话,只有选项 innodb_stats_transient_sample_pages 才能生效。

  • 选项 innodb_stats_transient_sample_pages 设定的是 动态 统计信息采集的data page数量,默认值是 8。

选项 innodb_stats_persistent_sample_pages 是全局作用的,但如果某个表想单独定义采集的page数目,可以在DDL时自行设定:

CREATE TABLE ... STATS_SAMPLE_PAGES = 30;

ALTER TABLE ... STATS_SAMPLE_PAGES = 30;

ANALYZE TABLE 代价估算

关于执行ANALYZE TABLE 的代价计算公式:
影响代价因素:

  • innodb_stats_persistent_sample_pages定义值大小;

  • 表中索引数多少;

  • 表中分区数多少。

代价粗略估算公式:innodb_stats_persistent_sample_pages * 索引数 * 分区数。

而更严谨的计算公式见下:

O(n_sample
* (n_cols_in_uniq_i
+ n_cols_in_non_uniq_i
+ n_cols_in_pk * (1 + n_non_uniq_i))
* n_part)

各项指标解释:

  • n_sample,采集的data page数量;

  • n_cols_in_uniq_i,所有唯一索引(不含主键索引)中的列总数;

  • n_cols_in_non_uniq_i,所有普通索引中的列总数;

  • n_cols_in_pk,主键索引中的列总数(若未显式定义主键,则相当于只有一列的ROWID);

  • n_non_uniq_i,非唯一索引数量;

  • n_part,表分区数量。

以下表为例:

CREATE TABLE t (
a INT,
b INT,
c INT,
d INT,
e INT,
f INT,
g INT,
h INT,
PRIMARY KEY (a, b),
UNIQUE KEY i1uniq (c, d),
KEY i2nonuniq (e, f),
KEY i3nonuniq (g, h)
);

我们执行下面的SQL来查询这个表的索引信息:

SELECT index_name, stat_name, stat_description
FROM mysql.innodb_index_stats
WHERE
database_name='test' AND
table_name='t' AND
stat_name like 'n_diff_pfx%';

+------------+--------------+------------------+
| index_name | stat_name | stat_description |
+------------+--------------+------------------+
| PRIMARY | n_diff_pfx01 | a |
| PRIMARY | n_diff_pfx02 | a,b |
| i1uniq | n_diff_pfx01 | c |
| i1uniq | n_diff_pfx02 | c,d |
| i2nonuniq | n_diff_pfx01 | e |
| i2nonuniq | n_diff_pfx02 | e,f |
| i2nonuniq | n_diff_pfx03 | e,f,a |
| i2nonuniq | n_diff_pfx04 | e,f,a,b |
| i3nonuniq | n_diff_pfx01 | g |
| i3nonuniq | n_diff_pfx02 | g,h |
| i3nonuniq | n_diff_pfx03 | g,h,a |
| i3nonuniq | n_diff_pfx04 | g,h,a,b |
+------------+--------------+------------------+

上面这个结果看起来有点奇怪是不是,其实没错,先科普几点知识:

  • 所有的普通索引,实际物理存储时,都要包含主键列的,也就是所谓的 index extensions 特性;

  • 统计索引信息时,是根据最左原则,要统计各种组合的。比如(a,b) 索引,要统计(a), (a,b), (a,b,pk) 三种信息,而不是只统计(a,b)这个信息;

  • 不过,在 mysql.innodb_index_stats 中存储统计信息时,是不统计唯一索引后面存储主键列信息的,非唯一普通索引后存储主键列信息则会被统计进去;

因此,上面 mysql.innodb_index_stats 中存储的统计结果是正确的。

我们再回来看下索引统计的代价公式,像下面这样计算:

- n_sample,采集的data page数量,值为 20(默认值);

- n_cols_in_uniq_i,所有唯一索引(不含主键索引)中的列总数,值为 2;

- n_cols_in_non_uniq_i,所有普通索引中的列总数,值为 4;

- n_cols_in_pk,主键索引中的列总数(若未显式定义主键,则相当于只有一列的ROWID),值为 2;

- n_non_uniq_i,非唯一索引数量,值为 2;

- n_part,表分区数量,值为 1(没有表分区,值为1,而不是0)。

那么最终需要扫描的data page数结果就是:

20 * (2 + 4 + 2 * (1 + 2)) * 1 = 240

实际需要读取的字节数则是:

240 * 16384 = 3932160 (即 3.84M)

当然了,要读取的data page,有可能已经在buffer pool中了,因此并不全是物理读。

从中,我们也可以看到,这个代价和表的数据量并无直接关系。
不过,当表数量越大时,聚集索引的 B+ 树也越大,搜索代价肯定也越大。

相关文章

pt-kill工具的使用
pt-ioprofile工具包的使用
数据库管理-第216期 Oracle的高可用-01(20240703)
DBMS_REPAIR EXAMPLE SCRIPT WITH PARTITION
数据库事务的四大特性: ACID 
使用BBED修复损坏的SYSTEM文件头

发布评论