首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL Bug #19528825 深度剖析:Insert Buffer 與 Purge 競態導致 "UNABLE TO PURGE A RECORD"

     編輯:MySQL 綜合教學

MySQL Bug #19528825 深度剖析:Insert Buffer 與 Purge 競態導致 "UNABLE TO PURGE A RECORD"

現象概述

生產環境開啟 innodb_change_buffering=all 時,偶發 UNABLE TO PURGE A RECORD 錯誤。官方於 2015 年 2 月修復(Bug #73767)。

背景知識:Insert Buffer (Change Buffer)

  • 針對非唯一二級索引的寫入緩衝。
  • 當目標頁不在 Buffer Pool 時,將操作(Insert/Delete/Update/Purge)暫存,待讀取頁時合併應用,減少隨機讀 IO。
  • 5.6 重命名為 Change Buffer,支援 Insert/Delete/Update/Purge 多種操作。
  • 操作碼對應:
    INSERT → IBUF_OP_INSERT
    DELETE → IBUF_OP_DELETE_MARK(打刪除標記)
    PURGE → IBUF_OP_DELETE(物理清理)
    UPDATE → IBUF_OP_DELETE_MARK + IBUF_OP_INSERT

Purge 機制

InnoDB MVCC 實現:刪除/更新僅打標記,後台 Purge 線程異步物理清理並回收 Undo Log 空間。

問題復現序列

假設表 t(id PK, c1 KEY),頁不在 Buffer Pool。
1. DELETE FROM t WHERE id=1; → IBUF_OP_DELETE_MARK
2. INSERT INTO t VALUES(1,'a'); → IBUF_OP_INSERT(新紀錄無刪除標記)
3. Purge 線程執行 IBUF_OP_DELETE,發現待刪紀錄無刪除標記 → 拋錯。

若順序為 1→3→2 則不報錯。根因:Purge 與用戶更新操作無嚴格同步,導致 Purge 清理到未打標記的新紀錄。

關鍵流程與協同機制

Purge 二級索引流程

row_purge_remove_sec_if_poss_leaf
  → row_search_index_entry
  → btr_pcur_open_func
  → btr_cur_search_to_nth_level
  → ibuf_should_try  (判斷是否用 ibuf)

Watch 標記協同

  • Purge 讀頁時若頁不在 Buffer Pool,設 Watch 標記,再 ibuf_insert 緩存 Purge 操作。
  • 用戶更新操作調用 ibuf_insert 時,先檢查 Watch 標記,若存在則放棄 ibuf,同步讀頁合併 Purge 操作,避免衝突。

為何正常路徑無此問題?

不走 ibuf 時,Purge 會調用 row_purge_poss_sec 確認聚簇索引記錄是否可 Purge(無刪除標記或 trx_id 較新則跳過),天然避開衝突。

修復方案核心

在 Purge 使用 ibuf 前,先 buf_page_get_gen 設 Watch,再調用 row_purge_poss_sec 判斷可否 Purge。若用戶已 Re-insert,Purge 直接忽略;否則繼續 ibuf_insert。
同時修正用戶更新操作:真正使用 ibuf 前必須重新檢查 Watch 標記,確保無 Purge 併發使用 ibuf。

官方修復提交:ec369cb

參考資源

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved