最近的MySQL8.0.14版本增加了其第一個并行查詢特性,可以支持在聚集索引上做SELECT COUNT()和check table操作。本文簡單的介紹下這個特性。
用法
增加了一個session級別參數:?innodb_parallel_read_threads
要執行并行查詢,需要滿足如下條件(ref:?row_scan_index_for_mysql
)
- 無鎖查詢
- 聚集索引
- 不是Insert...select
- 需要參數設置為>1
相關代碼
入口函數:
row_scan_index_for_mysqlparallel_select_count_star // for select count(*)parallel_check_table // for check table
innodb_buffer_pool_instances、InnoDB里實現了兩種查詢方式,一種是基于key的(key reader), 根據葉子節點上的值做分區,需要判斷可見性;另外一種是基于page的(physical read),根據page no來做分區,無需判斷可見性。目前支持的兩種查詢都是key reader的方式。
使用如下代碼創建一個reader,并調用接口函數,read()函數里的回調函數包含了如何對獲取到的行數據進行處理:
Key_reader reader(prebuilt->table, trx, index, prebuilt, n_threads);
reader.read(func), 其中func是回調函數,用于告訴線程怎么處理得到的每一行
分區并計算線程數
分區入口:
template <typename T, typename R>
typename Reader<T, R>::Ranges Reader<T, R>::partition()
流程:
- 搜集btree的最左節點page no
-
innodb_file_per_table?從root page開始向下,嘗試構建子樹:
- 如果該level的page個數不足線程數,繼續往下走
- 否則,使用該level, 搜集該level的每個page的最左記錄向下直到葉子節點的最左鏈表
-
如上搜集到的是多條代表自上而下的page no數組,需要根據這些數組創建分區range,這里有兩種創建方式:
-
Key_reader::Ranges Key_reader::create_ranges
: 基于鍵值創建分區- 找到每個鏈表的葉子節點的第一條記錄,存儲其cursor作為當前range的起點和上一個range的終點
-
Phy_reader::Ranges Phy_reader::create_ranges
:基于物理頁創建分區- 找到每個鏈表的葉子節點,相鄰鏈表的葉子節點組成一個range
-
MySQL update,線程數取分區數和配置線程數的最小值
啟動線程
啟動線程各自掃描:?start_parallel_load
為每個分區創建context(class Reader::Ctx),加入到隊列中
實現了一個Lock-free的隊列模型,多線程可以并發的從隊列中取context: 實現細節在文件include/ut0mpmcbq.h中,對應類?class mpmc_bq
, 實現思路見鏈接
線程函數:
dberr_t Reader<T, R>::worker(size_t id, Queue &ctxq, Function &f)
MySQL having。每取一個分區,調用處理函數去遍歷分區:
- Key_reader::traverse
對于獲得的每條記錄,判斷其可見性(共享事務對象trx_t),調用回調函數處理記錄(在Key_reader::read()作為參數傳遞),對于select count(*), 就是累加記錄的計數器 - Phy_reader::traverse
讀取每條非標記刪除的記錄并調用回調函數處理,無需判斷可見性
對于異常情況,只返回最后一個context的錯誤碼。
該特性只是MySQL在并行查詢的第一步,甚至定義了一些接口還沒有使用,例如接口函數pread_adapter_scan_get_num_threads
, 估計是給未來server層做并行查詢使用的。代碼里對應兩個適配類:
- Parallel_reader_adapter
- Parallel_partition_reader_adapter
另外一個可以用到的地方是創建二級索引,我們知道InnoDB創建二級索引,是先從聚集索引讀取記錄,生成多個merge file,然后再做歸并排序,但無論是生成merge file,還是排序,都可以做到并行化。官方也提到這是未來的一個優化點,相信不久的將來,我們就能看到MySQL更為強大的并行查詢功能。
Reference
innodb_thread_concurrency。WL#11720: InnoDB: Parallel read of index
MySQL 8.0.14: A Road to Parallel Query Execution is Wide Open!
本文作者:zhaiwx_yinfeng.
閱讀原文
本文為云棲社區原創內容,未經允許不得轉載。