写点什么

analyze 采样率是怎么算出来的(v6.5.3)

  • 2024-05-17
    北京
  • 本文字数:3778 字

    阅读完需:约 12 分钟

作者: Jayjlchen 原文来源:https://tidb.net/blog/62a094db

背景

在 TiDB 中,统计信息是查询优化器用来决定最佳查询执行计划的重要依据。准确的统计信息可以显著提高查询性能,因此定期的统计信息收集是维持数据库性能的重要任务之一。


统计信息收集过程中,analyze 操作会扫描表中的数据,并计算相关的统计数据。在此过程中,采样率(samplerate)是一个关键参数,它决定了需要扫描的数据量。理想情况下,采样率应当与表的大小成反比,以平衡统计信息的准确性和资源消耗。


最近有用户在 dashboard 发现了内存消耗高的 SQL,这是一个 analyze 收集统计信息的 SQL,涉及的表平均行长只有 600 bytes,并且采用低并发度参数的情况下,内存消耗接近 10G。我在 mysql.analyze_jobs 表中发现,当 processed_rows 高达 133 万时,采样率samplerate自适应地使用了 1。按预期,采样率应该是 11 万 / 133 万,即约 0.08,这导致实际使用的内存比预期高出许多。因此,我想了解一下 analyze 操作中的自适应采样率是如何计算出来的。


# 低并发参数下收集统计信息set session tidb_build_stats_concurrency=1;set session tidb_distsql_scan_concurrency=1;set session tidb_index_serial_scan_concurrency=1;analyze table xxx;
# mysql.analyze_jobs 查到的 job_info 信息analyze table all columns with 256 buckets, 500 topn, 1 samplerate
复制代码

源码分析过程

咨询相关产研大佬后,找到了对应的代码位置,在 getAdjustedSampleRate 模块,代码地址如下: https://github.com/pingcap/tidb/blob/v6.5.3/executor/builder.go#L2608-L2646



我们重点关注哪些情况下采样率 (sampleRate) 会被算成 1。


情形 1:stats_meta 没有值,且没有 PD 信息时,会返回默认 defaultRate (0.001)


if statsTbl == nil && !hasPD {    return defaultRate}
复制代码


情形 2:stats_meta 的 count 值为 0 且没有 PD 信息时,返回 1 ,也就是 samplerate 是 1


if statsTbl.Count == 0 && !hasPD {    return 1}
复制代码


情形 3:stats_meta 的 count 值乘以 5 ,如果仍小于存储获得的 approxiCount 值,那么:


  • 如果 150000/approxiCount 小于 1,那么返回值将是 150000/approxiCount,samplerate 将是一个零点几的小数。

  • 如果 150000/approxiCount 大于或等于 1,那么返回值将是 1,samplerate 是 1。


情形 4:stats_meta 的 count 值为 0 ,返回 1 ,也就是 samplerate 是 1


if statsTbl.Count == 0 {    return 1}
复制代码


情形 5:和经验值 11W 行做对比,返回零点几或者 1


  • 如果 110000/statsTbl.Count 小于 1,那么返回值将是 110000/statsTbl.Count,samplerate 将是一个零点几的小数。

  • 如果 110000/statsTbl.Count 大于或等于 1,那么返回值将是 1,samplerate 是 1。


注:可以简单理解为表数据量小于 11W,采样率就是 1,大于 11W 就是零点几。


return math.Min(x: 1, config.DefRowsForSampleRate/float64(statsTbl.Count))
复制代码

相关实验

注:为方便理解,这里准备了“情形 3”、“情形 4”和“情形 5”的 demo


情形 1-2 几乎没有或不易演示,略


情形 3


# lightning 设置 analyze = "off" 情况下导数 100w# 此时查询 Approximate Count for table TAB1 为 200wMySQL [test]> show stats_meta;+---------+------------+----------------+---------------------+--------------+-----------+| Db_name | Table_name | Partition_name | Update_time         | Modify_count | Row_count |+---------+------------+----------------+---------------------+--------------+-----------+| test    | TAB1       |                | 2024-05-16 21:48:44 |            0 |         0 |+---------+------------+----------------+---------------------+--------------+-----------+1 row in set (0.03 sec)
MySQL [test]> analyze table tab1;Query OK, 0 rows affected, 1 warning (3.75 sec)
# 第一次收集走“情形3”,采样率: 15w/200w=0.075MySQL [test]> show warnings;+-------+------+--------------------------------------------------------------------+| Level | Code | Message |+-------+------+--------------------------------------------------------------------+| Note | 1105 | Analyze use auto adjusted sample rate 0.075000 for table test.tab1 |+-------+------+--------------------------------------------------------------------+1 row in set (0.00 sec)
MySQL [test]> show stats_meta;+---------+------------+----------------+---------------------+--------------+-----------+| Db_name | Table_name | Partition_name | Update_time | Modify_count | Row_count |+---------+------------+----------------+---------------------+--------------+-----------+| test | TAB1 | | 2024-05-16 21:49:51 | 0 | 1000000 |+---------+------------+----------------+---------------------+--------------+-----------+1 row in set (0.00 sec)
MySQL [test]> analyze table tab1;Query OK, 0 rows affected, 1 warning (4.42 sec)
# 第二次收集走“情形5”,采样率: 11w/100w=0.11MySQL [test]> show warnings;+-------+------+--------------------------------------------------------------------+| Level | Code | Message |+-------+------+--------------------------------------------------------------------+| Note | 1105 | Analyze use auto adjusted sample rate 0.110000 for table test.tab1 |+-------+------+--------------------------------------------------------------------+1 row in set (0.00 sec)
复制代码


情形 4


MySQL [test]> create table tab2(id int);Query OK, 0 rows affected (0.12 sec)
MySQL [test]> show stats_meta where table_name='tab2';+---------+------------+----------------+---------------------+--------------+-----------+| Db_name | Table_name | Partition_name | Update_time | Modify_count | Row_count |+---------+------------+----------------+---------------------+--------------+-----------+| test | tab2 | | 2024-05-15 16:57:34 | 0 | 0 |+---------+------------+----------------+---------------------+--------------+-----------+1 row in set (0.03 sec)
MySQL [test]> analyze table tab2;Query OK, 0 rows affected, 1 warning (0.04 sec)
MySQL [test]> show warnings;+-------+------+--------------------------------------------------------------------+| Level | Code | Message |+-------+------+--------------------------------------------------------------------+| Note | 1105 | Analyze use auto adjusted sample rate 1.000000 for table test.tab2 |+-------+------+--------------------------------------------------------------------+1 row in set (0.00 sec)
复制代码


情形 5


# tab2 插入数据到17WMySQL [test]> show stats_meta where table_name='tab2';+---------+------------+----------------+---------------------+--------------+-----------+| Db_name | Table_name | Partition_name | Update_time         | Modify_count | Row_count |+---------+------------+----------------+---------------------+--------------+-----------+| test    | tab2       |                | 2024-05-15 17:13:20 |            0 |    170001 |+---------+------------+----------------+---------------------+--------------+-----------+1 row in set (0.00 sec)
MySQL [test]> analyze table tab2;Query OK, 0 rows affected, 1 warning (0.37 sec)
# 采样率 = 110000/170001 = 0.647055MySQL [test]> show warnings;+-------+------+--------------------------------------------------------------------+| Level | Code | Message |+-------+------+--------------------------------------------------------------------+| Note | 1105 | Analyze use auto adjusted sample rate 0.647055 for table test.tab2 |+-------+------+--------------------------------------------------------------------+1 row in set (0.00 sec)
复制代码

结论

我原本预计这个案例会走“情形 5”,但实际上它走了“情形 4”(通过排除法得出)。在 master 版本中,这部分代码已得到改进,并且会打印原因。目前的版本不容易排查,推测可能遇到了某些未知问题,导致统计信息被错误地覆盖成 0。很可能是遇到以下 issue 的问题,该问题将在 6.5.10 版本中得到修复:


https://github.com/pingcap/tidb/issues/52294


最后感谢 weizhen 大佬的支持!


发布于: 刚刚阅读数: 3
用户头像

TiDB 社区官网:https://tidb.net/ 2021-12-15 加入

TiDB 社区干货传送门是由 TiDB 社区中布道师组委会自发组织的 TiDB 社区优质内容对外宣布的栏目,旨在加深 TiDBer 之间的交流和学习。一起构建有爱、互助、共创共建的 TiDB 社区 https://tidb.net/

评论

发布
暂无评论
analyze 采样率是怎么算出来的(v6.5.3)_TiDB 源码解读_TiDB 社区干货传送门_InfoQ写作社区