MySQL核心月報2015.02-MySQL·答疑釋惑·InnoDB丟失自增值

db匠發表於2016-05-23

背景

在上一期的月報中,我們在InnoDB自增列重複值問題 中提到,InnoDB 自增列在重啟後會丟失,因為MySQL沒有持久化自增值,平時是存在記憶體表物件中的。如果例項重啟的話,記憶體值丟失,其初始化過程是做了一個類似 select max(id) + 1 操作。實際上存在另外一種場景,例項即使不重啟,也會導致自增值丟失。

問題說明

例項執行過種中,InnoDB表自增值是儲存在表物件中的,表物件又是放在快取中的,如果表太多而不能全部放在快取中的話,老的表就會被置換出來,這種被置換出來的表下次再使用的時候,就要重新開啟一遍,對自增列來說,這個過程就和例項重啟類似,需要 select max(id) + 1 算一下自增值。

對InnoDB來說,其資料字典中表物件快取大小由 table_definition_cache 系統變數控制,在5.6.8之後,其最小值是400。和表快取相關的另一個系統變數是table_open_cache,這個控制的是所有執行緒開啟表的快取大小,這個快取放在server層。

下面我們用testcase的方式來給出InnoDB表物件對置換出的場景:


可以看到自增值確實和重啟場景一樣,本應是100,卻變成了 2(select max(id) + 1)了。


問題分析

原因就是快取不夠,導致表物件被換出,下次再用就要重新開啟,這裡給出呼叫棧,對程式碼感興趣的同學可以看下。

將老的table置換出:


嘗試從快取載入表物件:


快取載入不到表物件,用select maxt 邏輯初始化自增:


處理建議

對於這個問題,一種解決方法是從原始碼改進,將自增值持久化,可以參考上期的月報給出的思路;如果不想改程式碼的話,可以這樣繞過:在設定auto_increment值後,主動插入一行記錄,這樣不論在重啟還是快取淘汰的情況下,重新開啟表仍能得到預期的值。


相關文章