Change Buffer 只適用於非唯一索引頁?錯

最近在網上看到一些文章裏說:“change buffer 只適用於非唯一索引頁。”其實這個觀點是錯的,先來看看官方文檔對 change buffer 的介紹:

文檔地址:https://dev.mysql.com/doc/refman/8.0/en/innodb-change-buffer.html

The change buffer is a special data structure that caches changes to secondary index pages when those pages are not in the buffer pool.

這裏的意思是,緩存那些不在 buffer pool 中的二級索引頁,並不是指非唯一的二級索引。那具體使用 change buffer 的條件是什麼?其實具體使用條件主要彙集在以下五點:

  • 用戶設置選項 innodb_change_buffering。

  • 在 mysql 的索引結構中,只有葉子結點才存儲數據。因此有葉子節點才考慮是否使用 ibuf。

  • 如上面文檔顯示的一樣,change buffer 只能緩存二級索引頁,所以對於聚集索引,不可以緩存操作。聚簇索引頁是由 Innodb 引擎將數據頁加載到 Buffer Pool中(這個查找過程是順序 I/O),然後進行數據記錄插入或者更新、刪除。

  • 因爲唯一二級索引(unique key)的索引記錄具有唯一性,因此無法緩存插入和更新操作,但可以緩存刪除操作;

  • 表上沒有 flush 操作,例如執行 flush table for export 時,不允許對錶進行 ibuf 緩存 (通過 dict_table_t::quiesce 進行標識)

接下來我們結合源碼和文檔來看看具體操作。

源碼地址:GitHub - mysql/mysql-server: MySQL Server, the world's most popular open source database, and MySQL

先看第一點設置選項尾 innodb_change_buffering,它能夠針對三種類型的操作 INSERT、DELETE-MARK 、DELETE 進行緩存,三者對應 dml 語句關係如下:

  • INSERT 操作:插入二級索引。

  • 先進行 DELETE-MARK 操作,再進行INSERT操作:更新二級索引。

  • DELETE-MARK 操作:刪除二級索引

// 代碼路徑:storage/innobase/include/ibuf0ibuf.h
 
/* Possible operations buffered in the insert/whatever buffer. See
ibuf_insert(). DO NOT CHANGE THE VALUES OF THESE, THEY ARE STORED ON DISK. */
typedef enum {
  IBUF_OP_INSERT = 0,
  IBUF_OP_DELETE_MARK = 1,
  IBUF_OP_DELETE = 2,

  /* Number of different operation types. */
  IBUF_OP_COUNT = 3
} ibuf_op_t;

/** Combinations of operations that can be buffered.
@see innodb_change_buffering_names */
enum ibuf_use_t {
  IBUF_USE_NONE = 0,
  IBUF_USE_INSERT,             /* insert */
  IBUF_USE_DELETE_MARK,        /* delete */
  IBUF_USE_INSERT_DELETE_MARK, /* insert+delete */
  IBUF_USE_DELETE,             /* delete+purge */
  IBUF_USE_ALL                 /* insert+delete+purge */
};

此外 innodb_change_buffering 還可以通過設置其他選項來進行相應的緩存操作:

  • all:默認值,默認開啓 buffer inserts、delete-marking operations、purges。

  • none:不開啓 change buffer。

  • inserts:只是開啓 buffer insert 操作。

  • deletes:只是開 delete-marking 操作。

  • changes:開啓 buffer insert 操作和 delete-marking 操作。

  • purges:對只是在後臺執行的物理刪除操作開啓 buffer 功能。

第二點還需要大家進行判斷條件即可,所以就不進行擴展講解了,我們來細說一下第三點。

第三點的具體參考函數爲 ibuf_should_try,它滿足 ibuf 緩存條件後,會使用兩種模式去嘗試獲取數據頁。

這裏說明一下,在 MySQL5.5 之前的版本中,由於只支持緩存 insert 操作,所以最初叫做 insert buffer,只是後來的版本中支持了更多的操作類型緩存,才改叫 change buffer,但是代碼中與 change buffer 相關的 函數或變量還是以 ibuf 前綴開頭。

下面是函數的具體實現,地址在:storage/innobase/include/ibuf0ibuf.ic

/** A basic partial test if an insert to the insert buffer could be possible and
 recommended. */
static inline ibool ibuf_should_try(
    dict_index_t *index,     /*!< in: index where to insert */
    ulint ignore_sec_unique) /*!< in: if != 0, we should
                             ignore UNIQUE constraint on
                             a secondary index when we
                             decide */
{
  return (innodb_change_buffering != IBUF_USE_NONE && ibuf->max_size != 0 &&
          index->space != dict_sys_t::s_dict_space_id &&
          !index->is_clustered() && !dict_index_is_spatial(index) &&
          !dict_index_has_desc(index) &&
          index->table->quiesce == QUIESCE_NONE &&
          (ignore_sec_unique || !dict_index_is_unique(index)) &&
          srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE);
}

上面加粗和標紅的地方就是對唯一二級索引的判斷的地方,意思是:

  • 當 ignore_sec_unique 這個變量爲 0 時,如果修改的是唯一二級索引記錄,就不能使用。

  • 當 ignore_sec_unique 這個變量爲 1 時,如果修改的是唯一二級索引記錄,還可以試着使用一下。

ignore_sec_unique 的取值在:storage/innobase/btr/btr0cur.cc

    if (btr_op != BTR_NO_OP &&
        ibuf_should_try(index, btr_op != BTR_INSERT_OP)) {
      /* Try to buffer the operation if the leaf
      page is not in the buffer pool. */

      fetch = btr_op == BTR_DELETE_OP ? Page_fetch::IF_IN_POOL_OR_WATCH
                                      : Page_fetch::IF_IN_POOL;
    }

其中紅框中的 btr_op 指的是本次修改的具體操作是什麼,也就是:

當此次具體的修改操作是 INSERT 操作時,ignore_sec_unique 爲 0,也就是當修改的是唯一二級索引記錄時,不可以使用 ibuf。

  • 當此次具體的修改操作不是 INSERT 操作時,ignore_sec_unique 爲 1,也就是當修改的是唯一二級索引記錄時,可以試着使用 ibuf。

  • 可以看到當 ibuf_should_try 函數返回 1 時,也就是可以試着用一下 ibuf,那麼就把讀取 buffer pool 中頁面的模式改一下。

最後總結一下,看完了本文相信大家都能比較明確的意識到,網上說只有非唯一索引才能使用 change buffer 的說法,毫無疑問是錯的。只要滿足了其它 4 個條件,對唯一索引進行的刪除操作完全可以使用 change buffer 優化。

推薦閱讀

javaScript 內存管理機制

130 行代碼搞定核酸統計,程序員在抗疫期間的大能量

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章