May 22, 2013

[MySQL 5.6] Innodb 后台线程之 dict stats 线程 及如何计算索引统计信息

前言   在5.6中,引入的一个新参数innodb_stats_auto_recalc用于控制是否进行自动统计信息计算。当表上的记录修改超过10%时,就会对统计信息重新计算;这只对在建表时打开了innodb_stats_persistent或者指定了建表选项STATS_PERSISTEND=1生效,采样page的个数通过参数innodb_stats_persistent_sample_pages来控制(实际读取的page数会大于该值)。 在函数dict_stats_is_persistent_enabled中可以看到,如果没有明确为ON/OFF的话(物理升级?),直接由innodb_stats_persistent来确定 我们也可以根据表的具体负载情况,通过alter语句为表设置是否打开或关闭STATS_PERSISTEND,因为统计信息的重计算,是一个开销很大的过程。 在MySQL 5.6中,完成统计信息自动计算的是一个独立线程,用于重新计算表或索引的统计信息.在一个loop中,等待事件dict_stats_event,或者每隔10秒被无条件唤醒。实际工作函数为dict_stats_process_entry_from_recalc_pool() 新对象:recalc_pool   1.recalc_pool   在内存中维持了这样一个vector,其定义如下:  57 typedef std::vector<table_id_t> recalc_pool_t;  58 static recalc_pool_t            recalc_pool; 所有需要被重新计算的表会加入到recalc_pool中,recalc_pool初始化大小为128,随后如果需要再被扩大。recalc_pool通过recalc_pool_mutex来进行互斥操作 2.将表加入到recalc_pool中 dict_stats_recalc_pool_add: 遍历recalc_pool,查看表的table->id是否已经存在,如果存在,直接返回,否则将该表的id加入到recalc_pool中,并发送dict_stats_event事件 在做完DML后,会去调用函数row_update_statistics_if_needed判断是否需要更新统计信息,有以下集中情况: 1)如果表明确指定了STATS_PERSISTEND或者打开了innodb_stats_persistent      (1)表上明确指定了stats_auto_recalc或者innodb_stats_auto_recalc为TRUE,并且修改的记录数大于总记录数的10分之一时,调用dict_stats_recalc_pool_add将表放到recalc_pool中,并将修改计数器table->stat_modified_counter重置为0.      (2)不满足(1),直接return,不进行下面的判断 2)这时候,在关闭了PERSISTENT STATS的情况下,当发现修改的记录超过6.25%时,更新统计信息dict_stats_update(table, DICT_STATS_RECALC_TRANSIENT)->dict_stats_update_transient 3.dict stat线程如何处理 dict stat线程接受到dict_stats_event事件后,调用工作函数dict_stats_process_entry_from_recalc_pool() 主要做以下几件事情: 1)从recalc_pool中拿第一个table id 2)持有dict->mutex锁 3)根据表id获取表对象(dict_table_open_on_id(table_id, TRUE, FALSE));如果表被drop掉了,这里返回的是NULL,不做任何处理 4)将表标记为table->stats_bg_flag = BG_STAT_IN_PROGRESS; 这样就无法去DROP 该表了。 5)现在可以安全的释放dict_sys->mutex 6)如果该表在10秒内 已经计算过一次,那么就把该表重新放到recalc_pool尾部,不做任何处理。否则: 调用dict_stats_update(table, […]