0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

MySQL 8.0 29 instant add/drop column性能回退問題

數(shù)據(jù)庫和存儲 ? 來源:MySQL內(nèi)核剖析 ? 2023-12-11 11:20 ? 次閱讀

影響范圍: 從 8.0.29 版本開始, 在read heavy 場景, 性能可能有 5%~10% 的性能回退

MySQL 官方在8.0.29 里面加了instant add/drop column 能力, 能夠?qū)崿F(xiàn) instant add 或者 drop cloumn 到表的任意位置. PolarDB 在這基礎(chǔ)上增加了可以 Instant 修改列的能力, 具體可以看我們的月報

instant DDL 核心觀點只有一個: don’t touch any row but update the metadata only, 也就是僅僅去修改 Data Dictionary(DD) 信息, 而不去修改數(shù)據(jù)信息,這樣才有可能做到 Instant.

具體的做法就是給每一個行增加了row_version, 然后DD 本身就是多版本, 不同的數(shù)據(jù)信息用不同的DD 信息去解析.

首先一個record 是否有row_version 信息添加到了Record info bits 里面.

info bits 包含有deleted flag, min record 等等信息, 后來在8.0.13 的時候增加record 是否有Instant ADD column 信息. 在 8.0.29 版本中增加了record 是否有 row_version 信息.

8976f392-97c9-11ee-8b88-92fbcf53809c.png

以上是這個 issue 背景, Instant add/drop column 的原理, 但是原因在哪里呢?

從Markus 提交上來的Flamegraph 可以看到, 在 8.0.33 里面 rec_get_offsets/cmp_dtuple_rec/rec_get_nth_field 等等相比 8.0.28 占比明顯增多了. 整個 row_serch_mvcc 的調(diào)用開銷也增加了.

899071f0-97c9-11ee-8b88-92fbcf53809c.png

899abade-97c9-11ee-8b88-92fbcf53809c.png

核心原因由于數(shù)據(jù)record 增加了 row_version 信息, 導致在執(zhí)行數(shù)據(jù)解析的函數(shù) rec_get_offsets/rec_get_nth_field 等函數(shù)中增加了很多額外的判斷, 并且官方把很多 inline function 改成了 non-inline.

為了驗證想法, 我們做了 3 個地方的修改, 具體可以看 Issue 上面的代碼提交:

1. 將一些 non-inline function 改回inline function

從 inline => non-inline. 修改的函數(shù)如下:

8.0.27

rec_get_nth_field => inline

rec_get_nth_field_offs => inline

rec_init_offsets_comp_ordinary => inline

rec_offs_nth_extern => inline

8.2.0

rec_get_nth_field => non-inline

rec_get_nth_field_offs => non-inline

rec_init_offsets_comp_ordinary => non-inline

rec_offs_nth_extern => non-inline

我們測試下來在 oltp_read_only 場景里面, 將這些 non-inline 函數(shù)改成 inline 以后, 性能可以有 3~5% 左右的提升空間. 具體改動代碼可以在 issue 里面獲得.

2. 簡化get_rec_insert_state 邏輯

8.0.29 增加了 get_rec_insert_state 函數(shù), 需要判斷當前 record 是來自哪一個版本升級上來的, 從而使用合適的 DD 代碼邏輯進行解析. 如果是包含有 row_version 版本, 還需要判斷是否帶有 version 信息, 如果沒有 version 信息, 是不是8.0.12 instant add column 版本等等, 這里的邏輯非?,嵥?

所以 REC_INSERT_STATE 的狀態(tài)非常多.

enum REC_INSERT_STATE {  /* Record was inserted before first instant add done in the earlier
  implementation. */
  INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION,  /* Record was inserted after first instant add done in the earlier
  implementation. */
  INSERTED_AFTER_INSTANT_ADD_OLD_IMPLEMENTATION,  /* Record was inserted after upgrade but before first instant add done in the
  new implementation. */
  INSERTED_AFTER_UPGRADE_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION,  /* Record was inserted before first instant add/drop done in the new
  implementation. */
  INSERTED_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION,  /* Record was inserted after first instant add/drop done in the new
  implementation. */
  INSERTED_AFTER_INSTANT_ADD_NEW_IMPLEMENTATION,  /* Record belongs to table with no verison no instant */
  // 如果index 上面沒有做過instant add 或者 最新的row_version 版本Instant add/drop
  INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION,
  NONE
};

具體獲得 insert_state 代碼:

static inline enum REC_INSERT_STATE get_rec_insert_state(    const dict_index_t *index, const rec_t *rec, bool temp) {
  ut_ad(dict_table_is_comp(index->table) || temp);  if (!index->has_instant_cols_or_row_versions()) {    return INSERTED_INTO_TABLE_WITH_NO_INSTANT_NO_VERSION;
  }  /* Position just before info-bits where version will be there if any */
  const byte *v_ptr =
      (byte *)rec -
      ((temp ? REC_N_TMP_EXTRA_BYTES : REC_N_NEW_EXTRA_BYTES) + 1);  const bool is_versioned =
      (temp) ? rec_new_temp_is_versioned(rec) : rec_new_is_versioned(rec);  // 如果有versioned 以后, 這里可以看到version 值是保存在Info bits 和 null field bitmap 中間的1 byte, 如下圖
  const uint8_t version = (is_versioned) ? (uint8_t)(*v_ptr) : UINT8_UNDEFINED;  const bool is_instant = (temp) ? rec_get_instant_flag_new_temp(rec)
                                 : rec_get_instant_flag_new(rec);  // 說明一個Record 不能同時被instalt add 和 row_version 版本instant add/drop 處理過
  // 應該以后默認的新版本是row_version 版本 instant add/drop, 老的要被淘汰
  if (is_versioned && is_instant) {
    ib::error() << "Record has both instant and version bit set in Table '"
                << index->table_name << "', Index '" << index->name()
                << "'. This indicates that the table may be corrupt. Please "
                   "run CHECK TABLE before proceeding.";
  }  enum REC_INSERT_STATE rec_insert_state = REC_INSERT_STATE::NONE;  if (is_versioned) {
    ut_a(is_valid_row_version(version));    if (version == 0) {
      ut_ad(index->has_instant_cols());      // is_versioned 說明record 有row_version, 如果version = 0, 說明是row_version DD 之前插入, 然后row_version DD 做過以后, 又升級了實例, 所以給這些row_version 設置成0
      rec_insert_state =
          INSERTED_AFTER_UPGRADE_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION;
    } else {      // 最正常的record, row_version DD 之后插入的, 有自己的row_version 版本
      ut_ad(index->has_row_versions());
      rec_insert_state = INSERTED_AFTER_INSTANT_ADD_NEW_IMPLEMENTATION;
    }
  } else if (is_instant) {    // 到這里說明record 上面沒有row_version DD 標記, 只有instant add 標記
    // 說明這個Record 是Instant add 之后插入的record, 并且沒有做過row_version DD
    ut_ad(index->table->has_instant_cols());
    rec_insert_state = INSERTED_AFTER_INSTANT_ADD_OLD_IMPLEMENTATION;
  } else if (index->table->has_instant_cols()) {    // 到這里說明record 上面 沒有row_version DD 和 instant add 標記, 但是這個index 上面有instant add 標記
    // 說明這個record 是instant add 之前就插入的
    rec_insert_state = INSERTED_BEFORE_INSTANT_ADD_OLD_IMPLEMENTATION;
  } else {    // record 上面沒有row_version DD, 也沒用instant add 標記, 并且index 上面也沒用instant add
    // 那么這個Record 是在row_version DD 以及 instant add 做過之前就插入的
    rec_insert_state = INSERTED_BEFORE_INSTANT_ADD_NEW_IMPLEMENTATION;
  }

  ut_ad(rec_insert_state != REC_INSERT_STATE::NONE);  return rec_insert_state;
}

這里雖然 inline enum REC_INSERT_STATE get_rec_insert_state 定義的是 inline, 但是其實這個只是代碼給編譯器的定義, 具體函數(shù)是否 Inline 其實是編譯器自己決定的, 最后其實具體運行的時候該函數(shù)并沒有 inline, 因為可以從Flamegraph 看到, 說明這個函數(shù)是有符號表的信息的, 因此肯定不是 inline 的

89bacf18-97c9-11ee-8b88-92fbcf53809c.png

3. 將 swatch case 改成 if/else, 并且給編譯器提示likely 執(zhí)行的 branch

最后我們發(fā)現(xiàn) switch case 對于有些明顯的分支預測并不友好, 通過 if/else 可以手動調(diào)整哪些 branch 更有可能執(zhí)行, 從而優(yōu)化編譯器的選擇.

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1602

    瀏覽量

    48896
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    789

    瀏覽量

    26283

原文標題:#issue 111538 MySQL 8.0 instant add/drop column 性能回退問題

文章出處:【微信號:inf_storage,微信公眾號:數(shù)據(jù)庫和存儲】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    MySQL8.0 新特性:Partial Update of LOB Column

    是標記刪除舊記錄,并插入新記錄,顯然這會帶來一些存儲上的開銷(盡管Purge線程會去后臺清理),而寫入的redo log和Binlog的量也會偏高,對于超大列,可能會嚴重影響到性能。MySQL8.0
    發(fā)表于 06-11 20:23

    ADD8504/ADD8505/ADD8506,pdf da

    The ADD8504, ADD8505, and ADD8506 are 4-, 5-, and 6-channel LCD gamma reference buffers designed
    發(fā)表于 09-02 16:16 ?15次下載

    即時效能與閃存微控制器-Instant Performanc

    flash and SRAM. These UHSFMs drop into existing 8051 applications and deliver an instant boost in speed. This application note discusse
    發(fā)表于 04-23 13:57 ?505次閱讀

    什么是ADM (Add/Drop Multiplexer)

    什么是ADM (Add/Drop Multiplexer)  英文縮寫: ADM (Add/Drop Multiplexer) 中文譯名: 分插復用器 分  類:
    發(fā)表于 02-22 10:10 ?2576次閱讀

    MySql5.6性能優(yōu)化最佳實踐

    MySql5.6性能優(yōu)化最佳實踐
    發(fā)表于 09-08 08:47 ?13次下載
    <b class='flag-5'>MySql</b>5.6<b class='flag-5'>性能</b>優(yōu)化最佳實踐

    Instant Raspberry Pi Gaming

    Instant Raspberry Pi Gaming
    發(fā)表于 10-24 09:26 ?6次下載
    <b class='flag-5'>Instant</b> Raspberry Pi Gaming

    騰訊云打造MySQL 8.0全新引擎,進一步加速客戶產(chǎn)業(yè)升級

    據(jù)介紹,騰訊云數(shù)據(jù)庫 MySQL 8.0的內(nèi)核可以百分百完全兼容主流MySQL分支。相比官方版本,無論是單機模式、異步模式還是同步模式下, MySQL
    的頭像 發(fā)表于 07-09 14:54 ?2270次閱讀

    MySQL 5.7與MySQL 8.0 性能對比

    背景 測試mysql5.7和mysql8.0分別在讀寫,選定,只寫模式下不同并發(fā)時的性能(tps,qps) 最早 測試使用版本為mysql5.7.22和
    的頭像 發(fā)表于 11-03 09:26 ?1.6w次閱讀
    <b class='flag-5'>MySQL</b> 5.7與<b class='flag-5'>MySQL</b> <b class='flag-5'>8.0</b> <b class='flag-5'>性能</b>對比

    MySql環(huán)境一鍵安裝應用程序免費下載

    本文檔的主要內(nèi)容詳細介紹的是MySql環(huán)境一鍵安裝應用程序免費下載。創(chuàng)建Mysql所需環(huán)境支持8.0以上版本,暫無測試過8.0以下版本
    發(fā)表于 02-26 15:01 ?7次下載

    關(guān)于MySQL8.0版本選型的小技巧

    MySQL 8.0 第一個GA(General Availability)版本(正式、可用于生產(chǎn)的版本)于2018/4/19發(fā)布至今已有3年。8.0是一個全新的版本,增加了數(shù)百項功能新特性,重構(gòu)了
    的頭像 發(fā)表于 03-29 13:45 ?1031次閱讀
    關(guān)于<b class='flag-5'>MySQL8.0</b>版本選型的小技巧

    請問mysql8.0不能在grant時創(chuàng)建用戶是什么原因?

    用習慣了MySQL5.7,當在MySQL8.0里創(chuàng)建用戶時,習慣性直接敲GRANT指令,結(jié)果報錯了
    的頭像 發(fā)表于 08-11 10:16 ?1912次閱讀

    mysql8.0默認字符集是什么

    MySQL 8.0 默認字符集是 utf8mb4。 MySQL 8.0 是當前最新的開源關(guān)系型數(shù)據(jù)庫管理系統(tǒng),由Oracle公司開發(fā)和維護。MySQ
    的頭像 發(fā)表于 11-16 14:48 ?1587次閱讀

    mysql數(shù)據(jù)庫的增刪改查sql語句

    MySQL是一種常用的關(guān)系型數(shù)據(jù)庫管理系統(tǒng),是許多網(wǎng)站和應用程序的首選數(shù)據(jù)庫。在MySQL中,我們可以使用SQL(結(jié)構(gòu)化查詢語言)進行數(shù)據(jù)的增刪改查操作。本文將詳細介紹MySQL數(shù)據(jù)庫的增刪改查
    的頭像 發(fā)表于 11-16 15:41 ?1017次閱讀

    MySQL5.7數(shù)據(jù)導入8.0版本,這3款工具值得收藏!

    MySQL 5.7數(shù)據(jù)庫遷移到MySQL 8.0可以使用NineData、MySQL Shell、Percona XtraBackup和Liquibase等工具。每個工具都有自己的優(yōu)
    的頭像 發(fā)表于 11-29 16:47 ?2059次閱讀
    <b class='flag-5'>MySQL</b>5.7數(shù)據(jù)導入<b class='flag-5'>8.0</b>版本,這3款工具值得收藏!

    GitHub底層數(shù)據(jù)庫無縫升級到MySQL 8.0的經(jīng)驗

    GitHub 團隊近日分享了他們將 GitHub.com 的底層數(shù)據(jù)庫無縫升級到 MySQL 8.0 的經(jīng)驗。 據(jù)介紹,GitHub 使用 MySQL 來存儲大量關(guān)系數(shù)據(jù),因此在不影響網(wǎng)站服務級別
    的頭像 發(fā)表于 12-13 10:21 ?394次閱讀
    GitHub底層數(shù)據(jù)庫無縫升級到<b class='flag-5'>MySQL</b> <b class='flag-5'>8.0</b>的經(jīng)驗