本文共 1329 字,大约阅读时间需要 4 分钟。
背景
为了提供优化器支持,InnoDB维护了每个表的索引统计信息(index statistics)。在之前的中作过介绍。
文章里面介绍到索引统计信息有几个更新策略,并建议允许关闭动态更新。顺便提及,5.6已经提供了Persistent Statistics 选项,达到相同的目的。
在5.5版本还是必须动态更新。本文介绍在动态更新时的一个问题。
索引统计的动态更新策略
这个策略是比较简单的。每个表维护一个stat_modified_counter,每次增、删、改一行时加1,当counter满足一下两个条件之一时,则进行依次动态统计,之后清0 (5.5.29)
1) 1) counter > 2000000000
2) 2) counter > 16 + table->stat_n_rows / 16
table->stat_n_rows表的总行数目(估计值)
row_update_statistics_if_needed( /*============================*/ dict_table_t* table) /*!< in: table */ { ulint counter;
counter = table->stat_modified_counter; table->stat_modified_counter = counter + 1;
if (counter > 2000000000 || ((ib_int64_t)counter > 16 + table->stat_n_rows / 16)) {
dict_update_statistics(table, FALSE /* update even if stats are initialized */); } |
执行索引统计的函数是dict_update_statistics,在执行统计期间,对stat加一个x锁。
存在问题
由于这个值不要求精确,因此在counter更新时并未加锁。这个问题并不大,但是未加锁导致另外一个问题:短时间内多次调用dict_update_statistics。
stat_modified_counter清0是在dict_update_statistics中调用的,但若有多个线程同时判断后进入dict_update_statistics,则会导致多次调用。
在一个测试场景我们从现场看到同一个时刻用11个用户线程调用dict_update_statistics。这意味着至少多调用了10次索引统计。而索引统计的方法是通过从表中取样本估算的,如果表数据较多,这个索引统计的浪费就很明显了。对查询和更新性能都会有影响。
简单修改
dict_update_statistics的逻辑流程是
1) 1) 加x锁
2) 2) 索引统计
3) 3) stat_modified_counter 置0
4) 4) 解锁
简单修改可以在加x锁之后再次判断stat_modified_counter是否需要进行索引统计,若不需要则解锁返回即可。
5.6已经提供了Persistent Statistics 选项,但若关闭,也会有这个问题。
转载地址:http://ijsgx.baihongyu.com/