歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> 學習Linux

悲劇啊!Mysql的上古BUG!!!

悲劇啊!Mysql的上古BUG!!!

悲劇啊!Mysql的上古BUG!!!


導讀這是MySQL8.0修復的上古bug之一,在2003年由Percona的CEO(當時應該還沒Percona吧)提出的bug#199,光看這bug號就撲面而來一股上古時代的滄桑氣息。

悲劇啊!Mysql的上古BUG!!!悲劇啊!Mysql的上古BUG!!!
問題的本質在於InnoDB初始化AUTO_INCREMENT的方式,在每次重啟時,總是算出表上最大的自增值作為最大值,下一次分配從該值開始。這意味著如果在btree右側葉節點大量刪除記錄,重啟後,自增值可能被重用。這在很多場景下可能導致問題,包括但不限於:主備切換、歷史數據遷移等場景。在bug#199下面一大堆的回復裡,可以看到大量的同行抱怨。

官方的修復就比較優雅了,不改變任何現有的存儲,而是通過redo log來進行恢復。該補丁基於WL#7816的框架實現的,要想搞懂這個補丁,得先看看WL#7816做了哪些改動,為了解決這個問題,InnoDB使用一個引擎私有的系統表+特殊redo log的方式,在引擎內部自己解決corruption標記持久化的問題。其大概思路為:

  1. 當發現索引損壞時,寫入一條redo log,但不更新數據詞典
  2. 引入一個innodb引擎私有的系統表,稱為DD Buffer Table,每次checkpoint之前會將索引corruption bit存入其中。
  3. 在崩潰恢復時,同時從redo log和DD Buffer Table中讀取索引 corruption bit, 合並結果,並標記內存中的表和索引對象。
初始化Persister

目前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中:

  1. 在做checkpoint(log_checkpoint)之前,所有在dirty_dict_tables鏈表上的表對象,對應persist metadata都需要回寫到DDTableBuffer中(dict_persist_to_dd_table_buffer)
  2. 從內存中驅逐一個表對象時(dict_table_remove_from_cache_low),如果需要的話也會去嘗試回寫。
  3. 在對包含自增列的表做DDL後,需要持久化counter,在如下函數中,會調用dict_table_set_and_persist_autoinc:
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):

  1. 通過表對象初始化需要回寫的Metadata數據: corrupt index及autoinc值(dict_init_dynamic_metadata)
  2. 構建記錄值,插入DDTableBuffer系統表(DDTableBuffer::replace(), 如果記錄存在的話,則進行悲觀更新操作
  3. 表對象的diry_status修改成 METADATA_BUFFERED,表示有buffer的元數據
Recovery and Startup

在崩潰恢復時,當解析到日志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

Copyright © Linux教程網 All Rights Reserved