導讀 | 這是MySQL8.0修復的上古bug之一,在2003年由Percona的CEO(當時應該還沒Percona吧)提出的bug#199,光看這bug號就撲面而來一股上古時代的滄桑氣息。 |
問題的本質在於InnoDB初始化AUTO_INCREMENT的方式,在每次重啓時,老是算出表上最大的自增值做爲最大值,下一次分配從該值開始。這意味着若是在btree右側葉節點大量刪除記錄,重啓後,自增值可能被重用。這在不少場景下可能致使問題,包括但不限於:主備切換、歷史數據遷移等場景。在bug#199下面一大堆的回覆裏,能夠看到大量的同行抱怨。html
官方的修復就比較優雅了,不改變任何現有的存儲,而是經過redo log來進行恢復。該補丁基於WL#7816的框架實現的,要想搞懂這個補丁,得先看看WL#7816作了哪些改動,爲了解決這個問題,InnoDB使用一個引擎私有的系統表+特殊redo log的方式,在引擎內部本身解決corruption標記持久化的問題。其大概思路爲:mysql
目前Persister的類型僅有兩種,一個用於corruption bit的持久化,一個用於自增列的持久化,對應的類爲:linux
Persister: |-- CorruptedIndexPersister |-- AutoIncPersister
Persister對應全局對象dict_persist_t::persisters,能夠經過類型persistent_type_t來找到對應的Persister,目前僅有PM_INDEX_CORRUPTED及PM_TABLE_AUTO_INC,但從註釋來看,將來確定會作更多的擴展,Persister在啓動時調用函數dict_persist_init進行初始化。sql
新的系統表名爲SYS_TABLE_INFO_BUFFER,對應管理類爲DDTableBuffer,指針存儲在dict_persist->table_buffer中。app
系統表包含兩個列:TABLE_ID及BLOB類型的METADATA(ref DDTableBuffer::init),METADATA列包含了全部須要持久化的元數據。框架
有幾種狀況會將內存修改回寫到DDTableBuffer中:函數
ha_innobase::commit_inplace_alter_table
create_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):spa
在崩潰恢復時,當解析到日誌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列,就老是選擇更大的那個值。rest
這個bug已經掛了至關長的時間,不排除把這個bug看成InnoDB的「特性」的同窗,必定要注意到這個改動...