問題的本質在於InnoDB初始化AUTO_INCREMENT的方式,在每次重啟時,總是算出表上最大的自增值作為最大值,下一次分配從該值開始。這意味著如果在btree右側葉節點大量刪除記錄,重啟後,自增值可能被重用。這在很多場景下可能導致問題,包括但不限於:主備切換、歷史數據遷移等場景。在bug#199下面一大堆的回復裡,可以看到大量的同行抱怨。
官方的修復就比較優雅了,不改變任何現有的存儲,而是通過redo log來進行恢復。該補丁基於WL#7816的框架實現的,要想搞懂這個補丁,得先看看WL#7816做了哪些改動,為了解決這個問題,InnoDB使用一個引擎私有的系統表+特殊redo log的方式,在引擎內部自己解決corruption標記持久化的問題。其大概思路為:
目前Persister的類型僅有兩種,一個用於corruption bit的持久化,一個用於自增列的持久化,對應的類為:
Persister: |-- CorruptedIndexPersister |-- AutoIncPersister
Persister對應全局對象dict_persist_t::persisters,可以通過類型persistent_type_t來找到對應的Persister,目前僅有PM_INDEX_CORRUPTED及PM_TABLE_AUTO_INC,但從注釋來看,未來肯定會做更多的擴展,Persister在啟動時調用函數dict_persist_init進行初始化。
新的系統表新的系統表名為SYS_TABLE_INFO_BUFFER,對應管理類為DDTableBuffer,指針存儲在dict_persist->table_buffer中。
系統表包含兩個列:TABLE_ID及BLOB類型的METADATA(ref DDTableBuffer::init),METADATA列包含了所有需要持久化的元數據。
回寫DDTableBuffer有幾種情況會將內存修改回寫到DDTableBuffer中:
ha_innobase::commit_inplace_alter_tablecreate_table_info_t::initialize_autoinc()// for example: alter table..auto_increment = ??row_rename_table_for_mysql;// rename from temporary table to normal table
回寫的過程也比較簡單(dict_table_persist_to_dd_table_buffer_low):
在崩潰恢復時,當解析到日志MLOG_TABLE_DYNAMIC_META時(MetadataRecover::parseMetadataLog),會進行解析並將解析得到的數據存儲到集合中(MetadataRecover::m_tables),如果存在相同table-id的項,就進行替換,確保總是最新的。
在完成recovery後,搜集到的meta信息暫時存儲到srv_dict_metadata中, 隨後進行apply(srv_dict_recover_on_restart), apply的過程也比較簡單,載入表對象,然後對表對象進行更新(MetadataRecover::apply),例如對於autoinc列,就總是選擇更大的那個值。
最後這個bug已經掛了相當長的時間,不排除把這個bug當作InnoDB的“特性”的同學,一定要注意到這個改動...
原文來自:https://yq.aliyun.com/articles/60885?utm_campaign=wenzhang&utm_medium=article&utm_source=QQ-qun&utm_content=m_6383
本文地址:http://www.linuxprobe.com/mysql-bug.html
http://xxxxxx/Linuxjc/1184592.html TechArticle