天天看點

MySQL · 8.0版本更新 · 性能優化篇

本文主要總結下mysql在8.0版本和性能相關的一些改動,随着新的小版本的釋出,本文将不斷進行更新,直到正式ga。

| <code>mysql 8.0.0</code>

在wl#9387中,在parse undo log時,通過table_id進行分組存儲,在分發時確定同一個table id的記錄被配置設定給同一個線程。(參考函數 <code>trx_purge_attach_undo_recs</code>)

當然這也意味着合理的不會産生沖突的單表操作, 無法利用到多線程purge了,也算是一個弊端。

name

desc

<code>buf_pool_t::lru_list_mutex</code>

用于保護lru連結清單,例如從lru連結清單上刷髒或驅逐page

<code>buf_pool_t::free_list_mutex</code>

保護free list及withdraw list(online resize)

<code>buf_pool_t::zip_free_mutex</code>

保護zip_free數組,該數組用于維護對壓縮表産生的非标準page size的記憶體維護, ref buf/buf0buddy.cc

<code>buf_pool_t::zip_hash_mutex</code>

保護zip_hash, 其中存儲壓縮頁block

<code>buf_pool_t::flush_state_mutex</code>

保護init_flush, n_flush, no_flush等數組

配置設定空閑block(<code>buf_lru_get_free_block</code>):

從free list擷取: buf_pool_t::free_list_mutex

從unzip_lru/lru上驅逐一個空閑page,需要buf_pool_t::lru_list_mutex

批量掃描lru(<code>buf_do_lru_batch</code>): buf_pool_t::lru_list_mutex

批量掃描flush_list(<code>buf_do_flush_list_batch</code>): buf_pool_t::flush_list_mutex

髒頁加入到flush_list(<code>buf_flush_insert_into_flush_list</code>): buf_pool_t::flush_list_mutex

髒頁寫回磁盤後,從flush list上移除(<code>buf_flush_write_complete</code>): buf_pool_t::flush_state_mutex/flush_list_mutex

從lru上驅逐page(<code>buf_lru_free_page</code>):buf_pool_t::lru_list_mutex, 及buf_pool_t::free_list_mutex(<code>buf_lru_block_free_non_file_page</code>)

<code>buf_flush_lru_list_batch</code> 使用mutex_enter_nowait 來擷取block鎖,如果擷取失敗,說明正被其他session占用,忽略該block.

有些變量的修改從通過buf_pool_t::mutex保護,修改成通過memory barrior來保護(<code>os_rmb</code> or <code>os_wmb</code>), 例如下面幾個函數中均有展現:

通過對鎖的拆分,降低了全局大鎖的競争,提升了buffer pool的擴充性,這個特性其實在percona server中很多年前就有了, 但直到mysql8.0版本才合并進來。

主要是用于為優化器提供更準确的資訊,即資料是存在與磁盤還是記憶體中, 這樣優化器可以更準确的做出代價計算。

增加一個全局對象(<code>buf_stat_per_index_t</code>)來管理所有的索引頁計數

為了避免引入新的全局鎖開銷,實作并使用一個lock-free的hash結構(<code>"include/ut0lock_free_hash.h</code>)來存儲索引資訊,key值為索引id。(目前索引id具有唯一性,但不排除未來可能發生改變)。

增加計數:

page剛從磁盤讀入記憶體 (<code>buf_page_io_complete --&gt; buf_page_monitor</code>)

建立一個新的page (<code>btr_page_create</code>)

遞減計數: page從lru上釋放時進行遞減(<code>buf_lru_block_remove_hashed</code>)

增加新的information_schema.innodb_cached_indexs 列印每個索引在記憶體中的page個數,其結構如下:

為了減少對btree的鎖占用,innodb在讀取資料時實際上是有一個小的緩存buffer。對于連續記錄掃描,innodb在滿足比較嚴格的條件時采用row cache的方式連續讀取8條記錄(并将記錄格式轉換成mysql format),存儲線上程私有的<code>row_prebuilt_t::fetch_cache</code>中;這樣一次尋路就可以擷取多條記錄,在server層處理完一條記錄後,可以直接從cache中取資料而無需再次尋路,直到cache中資料取完,再進行下一輪。

在wl#7093中引入了新的接口,由于優化器可以估算可能讀取的行數,是以可以提供給存儲引擎一個更合适大小的row buffer來存儲需要的資料。大批量的連續資料掃描的性能将受益于更大的record buffer。

record buffer由優化器來自動決定是否開啟,增加新的類<code>record_buffer</code>進行管理, record buffer的大小最大不超過128kb, 目前是hard code的,不可以配置。

判斷及配置設定record buffer函數: <code>set_record_buffer</code>, 并通過新的api接口(<code>handler::ha_set_record_buffer</code>)傳到引擎層

buffer本身是引擎無關的,在sever層配置設定,通過handler成員m_record_buffer傳遞到引擎層。

增加新的接口,判斷是否支援record buffer, 目前僅innodb支援,需要滿足如下條件 (ref <code>set_record_buffer</code>):

access type 不是 ref, ref_or_null, index_merge, range, index 或者all

不是臨時表

不是loose index scan

進入innodb引擎層判斷(<code>(</code>row_prebuilt_t::can_prefetch_records<code>)</code>)

在innodb中,當record buffer被配置時,就使用server層提供的record buffer,而不是<code>row_prebuilt_t::fetch_cache</code>

該worklog的目的是改進短連接配接場景下的性能,對thd list的操作可能導緻比較高的鎖競争。

解決方案也比較傳統,就是進行分區,将連結清單thd_list劃分成多個數組,目前為8個分區,相應的鎖<code>lock_thd_remove</code>和<code>lock_thd_list</code>鎖也進行了分區。