中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

關于MySQL8.0 InnoDB并行執行的詳解

發布時間:2020-03-27 11:06:15 來源:億速云 閱讀:1007 作者:小新 欄目:MySQL數據庫

今天小編給大家分享的是關于MySQL8.0 InnoDB并行執行的詳解,很多人都不太了解,今天小編為了讓大家更加了解MySQL8.0,所以給大家總結了以下內容,一起往下看吧。一定會有所收獲的哦。

概述

MySQL經過多年的發展已然成為最流行的數據庫,廣泛用于互聯網行業,并逐步向各個傳統行業滲透。之所以流行,一方面是其優秀的高并發事務處理的能力,另一方面也得益于MySQL豐富的生態。MySQL在處理OLTP場景下的短查詢效果很好,但對于復雜大查詢則能力有限。最直接一點就是,對于一個SQL語句,MySQL最多只能使用一個CPU核來處理,在這種場景下無法發揮主機CPU多核的能力。MySQL沒有停滯不前,一直在發展,新推出的8.0.14版本第一次引入了并行查詢特性,使得check table和select count(*)類型的語句性能成倍提升。雖然目前使用場景還比較有限,但后續的發展值得期待。

使用方式

通過配置參數innodb_parallel_read_threads來設置并發線程數,就能開始并行掃描功能,默認這個值為4。我這里做一個簡單的實驗,通過sysbench導入2億條數據,分別配置innodb_parallel_read_threads為1,2,4,8,16,32,64,測試并行執行的效果。測試語句為select count(*) from sbtest1;

關于MySQL8.0 InnoDB并行執行的詳解

橫軸是配置并發線程數,縱軸是語句執行時間。從測試結果來看,整個并行表現還是不錯的,掃描2億條記錄,從單線程的18s,下降到32線程的1s。后面并發開再多,由于數據量有限,多線程的管理消耗超過了并發帶來的性能提升,不能再繼續縮短SQL執行時間。

MySQL并行執行

實際上目前MySQL的并行執行還處于非常初級階段,如下圖所示,左邊是之前MySQL串行處理單個SQL形態;中間的是目前MySQL版本提供的并行能力,InnoDB引擎并行掃描的形態;最右邊的是未來MySQL要發展的形態,優化器根據系統負載和SQL生成并行計劃,并將分區計劃下發給執行器并行執行。并行執行不僅僅是并行掃描,還包括并行聚集,并行連接,并行分組,以及并行排序等。目前版本MySQL的上層的優化器以及執行器并沒有配套的修改。因此,下文的討論主要集中在InnoDB引擎如何實現并行掃描,主要包括分區,并行掃描,預讀以及與執行器交互的適配器類。

關于MySQL8.0 InnoDB并行執行的詳解

分區

并行掃描的一個核心步驟就是分區,將掃描的數據劃分成多份,讓多個線程并行掃描。InnoDB引擎是索引組織表,數據以B+tree的形式存儲在磁盤上,節點的單位是頁面(block/page),同時緩沖池中會對熱點頁面進行緩存,并通過LRU算法進行淘汰。分區的邏輯就是,從根節點頁面出發,逐層往下掃描,當判斷某一層的分支數超過了配置的線程數,則停止拆分。在實現時,實際上總共會進行兩次分區,第一次是按根節點頁的分支數劃分分區,每個分支的最左葉子節點的記錄為左下界,并將這個記錄記為相鄰上一個分支的右上界。通過這種方式,將B+tree劃分成若干子樹,每個子樹就是一個掃描分區。經過第一次分區后,可能出現分區數不能充分利用多核問題,比如配置了并行掃描線程為3,第一次分區后,產生了4個分區,那么前3個分區并行做完后,第4個分區至多只有一個線程掃描,最終效果就是不能充分利用多核資源。

二次分區

為了解決這個問題,8.0.17版本引入了二次分區,對于第4個分區,繼續下探拆分,這樣多個子分區又能并發掃描,InnoDB引擎并發掃描的最小粒度是頁面級別。具體判斷二次分區的邏輯是,一次分區后,若分區數大于線程數,則編號大于線程數的分區,需要繼續進行二次分區;若分區數小于線程數且B+tree層次很深,則所有的分區都需要進行二次分區。

相關代碼如下:

split_point = 0;
if (ranges.size() > max_threads()) {
   //最后一批分區進行二次分區                                      
   split_point = (ranges.size() / max_threads()) * max_threads();          
 } else if (m_depth < SPLIT_THRESHOLD) {                                  
   /* If the tree is not very deep then don't split. For smaller tables    
   it is more expensive to split because we end up traversing more blocks*/
   split_point = max_threads();                                            
 } else {
   //如果B+tree的層次很深(層數大于或等于3,數據量很大),則所有分區都需要進行二次分區
 }

無論是一次分區,還是二次分區,分區邊界的邏輯都一樣,以每個分區的最左葉子節點的記錄為左下界,并且將這個記錄記為相鄰上一個分支的右上界。這樣確保分區足夠多,粒度足夠細,充分并行。下圖展示了配置為3的并發線程,掃描進行二次分區的情況。

相關代碼如下:

關于MySQL8.0 InnoDB并行執行的詳解

create_ranges(size_t depth, size_t level)
一次分區:
parallel_check_table
 add_scan
   partition(scan_range, level=0)  /* start at root-page */
     create_ranges(scan_range, depth=0, level=0)
   create_contexts(range, index >= split_point)
二次分區:                                                      
split()
 partition(scan_range, level=1)
   create_ranges(depth=0,level)

并行掃描

在一次分區后,將每個分區掃描任務放入到一個lock-free隊列中,并行的worker線程從隊列中獲取任務,執行掃描任務,如果獲取的任務帶有split屬性,這個時候worker會將任務進行二次拆分,并投入到隊列中。這個過程主要包括兩個核心接口,一個是工作線程接口,另外一個是遍歷記錄接口,前者從隊列中獲取任務并執行,并維護統計計數;后者根據可見性獲取合適的記錄,并通過上層注入的回調函數處理,比如計數等。

Parallel_reader::worker(size_t thread_id)

{

1.從ctx-queue提取ctx任務

2.根據ctx的split屬性,確定是否需要進一步拆分分區(split())

3.遍歷分區所有記錄(traverse())

4.一個分區任務結束后,維護m_n_completed計數

5.如果m_n_compeleted計數達到ctx數目,喚醒所有worker線程結束

6.根據traverse接口,返回err信息。

}

Parallel_reader::Ctx::traverse()

{

1.根據range設置pcursor

2.找到btree,將游標定位到range的起始位置

3.判斷可見性(check_visibility)

4.如果可見,根據回調函數計算(比如統計)

5.向后遍歷,若達到了頁面的最后一條記錄,啟動預讀機制(submit_read_ahead)

6.超出范圍后結束

}

同時在8.0.17版本還引入了預讀機制,避免因為IO瓶頸導致并行效果不佳的問題。目前預讀的線程數不能配置,在代碼中硬編碼為2個線程。每次預讀的單位是一個簇(InnoDB文件通過段,簇,頁三級結構管理,一個簇是一組連續的頁),根據頁面配置的大小,可能為1M或者2M。對于常見的16k頁面配置,每次預讀1M,也就是64個頁面。worker線程在進行掃描時,會先判斷相鄰的下一個頁面是否為簇的第一個頁面,如果是,則發起預讀任務。預讀任務同樣通過lock-free 隊列緩存,worker線程是生產者,read-ahead-worker是消費者。由于所有分區頁面沒有重疊,因此預讀任務也不會重復。

執行器交互(適配器)

實際上,MySQL已經封裝了一個適配器類Parallel_reader_adapter來供上層使用,為后續的更豐富的并行執行做準備。首先這個類需要解決記錄格式的問題,將引擎層掃描的記錄轉換成MySQL格式,這樣做到上下層解耦,執行器不用感知引擎層格式,統一按MySQL格式處理。整個過程是一個流水線,通過一個buffer批量存儲MySQL記錄,worker線程不停的將記錄從引擎層上讀上來,同時有記錄不停的被上層處理,通過buffer可以平衡讀取和處理速度的差異,確保整個過程流動起來。緩存大小默認是2M,根據表的記錄行長來確定buffer可以緩存多少個MySQL記錄。核心流程主要在process_rows接口中,流程如下

process_rows

{

1.將引擎記錄轉換成MySQL記錄

2.獲取本線程的buffer信息(轉換了多少mysql記錄,發送了多少給上層)

3.將MySQL記錄填充進buffer,自增統計m_n_read

4.調用回調函數處理(比如統計,聚合,排序等),自增統計m_n_send

}

對于調用者來說,需要設置表的元信息,以及注入處理記錄回調函數,比如處理聚集,排序,分組的工作。回調函數通過設置m_init_fn,m_load_fn和m_end_fn來控制。

總結

MySQL8.0引入了并行查詢雖然還比較初級,但已經讓我們看到了MySQL并行查詢的潛力,從實驗中我們也看到了開啟并行執行后,SQL語句執行充分發揮了多核能力,響應時間急劇下降。相信在不久的將來,8.0的會支持更多并行算子,包括并行聚集,并行連接,并行分組以及并行排序等。

看完上文,你對關于MySQL8.0 InnoDB并行執行大概了解了嗎?如果想了解更多,歡迎關注億速云行業資訊頻道哦

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

吉林省| 即墨市| 东乡族自治县| 西乌珠穆沁旗| 天全县| 庆城县| 巨野县| 朝阳区| 嘉义市| 革吉县| 普陀区| 怀来县| 玉山县| 都江堰市| 靖江市| 武穴市| 崇州市| 丹凤县| 武清区| 乌兰浩特市| 保康县| 福贡县| 卢龙县| 鄂伦春自治旗| 金溪县| 佛山市| 江西省| 绥芬河市| 广丰县| 河津市| 嘉黎县| 景德镇市| 盘山县| 巴塘县| 青浦区| 墨玉县| 新宁县| 邢台县| 江陵县| 盘锦市| 高青县|