溫故知新--EverDB之分布式執行計劃
?在數據庫系統設計中,執行計劃是對SQL執行流程的形式化描述,包括了SQL執行需要的所有算子以及其執行次序。我們通過“EXPLAIN + SQL”指令可以詳細地查看其執行計劃,找到性能瓶頸,為我們優化SQL提供方向和依據。本文將從EverDB分布式數據庫角度闡述執行計劃。
(一)分布式架構執行計劃
相比于集中式數據庫,分布式數據庫擁有大量分片節點,分別負責各自分片的數據計算與存儲,那么其執行計劃就需要特殊的實現方式。對于中間件架構分布式數據庫,通過引入分布式算子(即下文EverDB執行計劃節點)實現數據分片存儲功能,執行計劃解析優化,下發數據分片內部獨立計算,協調數據分片之間并發執行,執行結果由中間件進行進一步整合進行分組、排序等操作,是一種高效便捷的實現方式。
EverDB正是基于這種設計思路實現的執行計劃。與傳統集中式數據庫相比,EverDB執行計劃使數據庫有更高的擴展性,支持更大量級的數據規模,更高并發的數據訪問。在處理相同負載壓力的前提下,可以充分利用各分片的存儲與計算資源,以及并行計算的優勢達到更好的性能。
(二)EverDB執行計劃
EverDB分布式數據庫由Grid調度層、數據節點、配置節點、管理臺組成。Grid調度層作為分布式數據庫的調度節點,接收并解析SQL,將SQL語句重構改造,支持涉及分片表和非分片表兩種類型的執行計劃分析。
圖1
EverDB的執行計劃包括SQL在Grid調度層和后端數據節點的執行流程。Grid調度節點的執行計劃,主要涉及邏輯處理層和連接驅動層兩個部分,其中邏輯處理層包括詞法、語法解析模塊,客戶端通信模塊,普通表/分片表配置、SQL重構改造、執行計劃樹及計劃樹節點。其中普通表/分片表配置用于識別SQL是否需要分片處理,并獲取分片表的存儲地址信息,完成基于分片策略的執行計劃構建。連接驅動層是內部連接池和通信協議的處理模塊,完美支持MySQL通信協議,負責在執行計劃中將請求下推給數據節點。數據節點執行計劃的實現方式可以參照MySQL執行計劃。
圖2
以分片查詢為例,EverDB的Grid調度節點的執行計劃流程:
SQL解析:客戶端處理線程接收到從客戶端發來的查詢請求,對SQL進行詞法語法解析。
SQL重構:根據SELECT查詢表的存儲信息,可分為普通表和分片表,如果是分片表,需要進一步根據查詢條件和數據存儲情況,重構優化SQL語句。比如,多分片間的跨節點查詢,可通過SQL重構后下推數據節點執行,或者通過建立臨時表,遷移小部分數據來降低查詢性能損耗。
構建執行計劃:SQL經過解析,需要構建對應的執行計劃樹,即用于維護SQL執行計劃的數據結構,由多個執行計劃節點構成。執行計劃節點是SQL執行過程中每一步操作的執行者,也可以看作一個個線程的執行體,它分為很多類型,用于執行不同的操作,比如內部執行節點、事務執行計劃節點、數據遷移執行節點、信息查詢節點、信息發送節點、組合排序去重節點等。
運行執行計劃:執行計劃運行過程中,對于分片表查詢,采用多線程并發的方式,加快分布式集群的處理速度。
SQL下推:為將查詢請求下推至對應的分片數據節點,EverDB通過通信模塊(即圖3中的MySQL協議適配、驅動模塊)將查詢請求以MySQL通信協議的格式封裝成數據包,再由連接池分配的連接將數據包發送給數據節點,以完成分片查詢請求的下推。
整合結果:數據節點接收到來自調度節點的請求,進行進一步的SQL解析,形成針對表的執行計劃。查詢計算完成后,數據節點將查詢結果反饋至調度節點,由調度節點繼續按執行計劃樹,對所有數據節點返回的分片結果進行歸并、排序等操作,將完整的查詢結果返回給客戶端,完成查詢請求。
圖3
調度節點在生成執行計劃樹時,會根據分片規則對語句進行并行執行改造,將重構后的多條SQL由對應的執行計劃樹葉節點下推至目標實例,由數據節點實例完成該分片的查詢執行計劃分析。
圖4介紹了執行計劃葉節點將查詢請求下推至數據節點的通訊流程。COM_QUERY是封裝了查詢語句的協議包,由執行計劃樹葉節點發送至對應的數據節點進行查詢計算。執行計劃葉節點以MySQL協議流程接收、解析結果集。圖示中結果集返回的協議包及次序為:
ResultSetHead:結果集頭包,包含列個數信息;
Field:結果集字段包,包含每一字段具體的信息,結果集每一字段對應一個Field協議包;
所有字段信息發送結束后,后端數據節點發送一個 EOF 協議包,開始行數據的發送;
RowData:結果集行數據包,與Field協議包相同,每一行的數據對應一個行數據包,因此,一次結果集發送可能會包含多個行數據協議包;
所有行數據包發送完畢后,服務端會再發送 EOF 協議包表示結果集發送的結束;
執行計劃葉節點收到分片的查詢結果后,將各自分片結果交由父級非葉節點對所有分片結果做進一步處理(如歸并、排序等),向客戶端返回完整的查詢數據結果。
圖4
(三) 如何查看執行計劃?
展示執行計劃,只需在查詢的SELECT關鍵字之前增加DBSCALE EXPLAIN。具體語法如下:
DBSCALE EXPLAIN + SELECT查詢語句;
結果包含執行計劃每一步的執行信息,顯示執行節點、執行次序和執行SQL內容,SQL性能好壞也能通過執行計劃看出來。用于分析SQL語句和表結構的性能瓶頸。
圖5
如上圖(圖5)示例,執行計劃返回結果分為上下兩個結果集。第一部分展示的是查詢請求從中間層Grid到數據節點的完整執行計劃。結果集前兩列是SQL在中間層Grid的執行計劃,即exec_node字段展示SQL的執行計劃樹,data_source展示的是每一個分片執行節點涉及的分片數據源。結果集其他字段則展示的是每一條分片查詢在各自數據節點上的執行計劃,這塊與MySQLexplain的返回結果是相同的。第二部分展示的是執行計劃在每個執行節點上實際運行的重構后SQL語句,因此可能與從客戶端接收到的SQL語句不同。
執行計劃中一些重要字段的說明如下:
- exec_node:執行計劃樹的每一個執行節點。整列展示了完整的執行計劃樹,以“*”開頭表示執行計劃樹根節點,“-”開頭表示執行計劃樹子節點,其中短橫線越長表示節點層數越深。如上文示例包含*MySQLSendNodeid首字母為*號,是此例分片查詢執行計劃樹的根節點。--MySQLFetchNode以“--”開頭,是執行計劃樹的子節點,多個FetchNode并發查詢對應數據節點的數據分片,再由SendNode整合多個FetchNode的查詢結果。
- data_source:數據源信息。數據源是提供數據庫連接用來具體執行客戶端請求的數據庫實例,即MySQLFetchNode執行查詢的實例地址。
- id:查詢中執行select子句或操作表的順序,id相同,執行順序由上至下;id不同,id值越大優先級越高,越先被執行。
- select_type:查詢數據的操作類型,如下表:
SIMPLE | 查詢中不包含子查詢或者UNION |
PRIMARY | 查詢中若包含任何復雜的子部分,最外層查詢標記為PRIMARY |
SUBQUERY | 在SELECT或WHERE列表中包含了子查詢,該子查詢被標記為SUBQUERY |
DERIVED | 在FROM列表中包含的子查詢被標記為DERIVED(衍生) |
UNION | 若第二個SELECT出現在UNION之后,則被標記為UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為DERIVED |
UNION RESULT | 從UNION表獲取結果的SELECT被標記為UNION RESULT |
- table:執行節點所處理的表名。
- type:數據節點在表中找到所需行的方式,又稱“訪問類型”,表示| All | index | range | ref | eq_ref | const,system | null | 由左至右,由最差到最好。常見類型如下表:
ALL | Full Table Scan, 數據節點將遍歷全表以找到匹配的行 |
Index | Full Index Scan,index與ALL區別為index類型只遍歷索引樹 |
Range | 索引范圍掃描,對索引的掃描開始于某一點,返回匹配值域的行,常見于between、<、>等的查詢 |
Ref | 非唯一性索引掃描,返回匹配某個單獨值的所有行。常見于使用非唯一索引即唯一索引的非唯一前綴進行的查找 |
Eq_ref | 唯一性索引掃描,對于每個索引鍵,表中只有一條記錄與之匹配;常見于主鍵或唯一索引掃描 |
Const、system | 當數據節點對查詢某部分進行優化,并轉換為一個常量時,使用這些類型訪問;如將主鍵置于where列表中,數據節點就能將該查詢轉換為一個常量,system是const類型的特例,當查詢的表只有一行的情況下,使用system |
NULL | 數據節點在優化過程中分解語句,執行時甚至不用訪問表或索引 |
- possible_keys:指出數據節點能使用哪個索引在表中找到行,查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢使用。
- key:顯示數據節點在查詢中實際使用的索引,若沒有使用索引,顯示為NULLNote:查詢中若使用了覆蓋索引,則該索引僅出現在key列表中。
- key_len:表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度。key_len顯示的值為索引字段的最大可能長度,并非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的。
- ref:表示上述表的連接匹配條件,即哪些列或常量被用于查找索引列上的值。
- rows:表示數據節點根據表統計信息及索引選用情況,估算的找到所需的記錄所需要讀取的行數。
- Extra:數據節點解決查詢的詳細信息,盡量避免出現:Using File Sort、Using Temporary。
第二部分包括node_id和sql兩個字段:node_id與第一部分中exec_node字段的中括號內序號相關聯,表示exec_node每個層次中具體執行SQL語句。具體SQL語句內容則在“sql”字段中顯示。
當“sql”字段中出現臨時表dbscale_tmp時(dbscale_tmp為EverDB保留字),說明當前SELECT查詢涉及跨分片查詢,系統性能損耗較高,需要進一步分析SQL語句和表結構性能瓶頸,盡可能避免使用臨時表,示例如下。
? ?
圖6
四 總結
EverDB作為一種典型的基于中間件實現分庫分表方案的分布式數據庫產品,其執行計劃相比于傳統集中式數據庫的不同之處在于,既包括了SQL在底層各分片表上的執行步驟,也包含proxy如何將SQL進行分布式處理,提高分布式數據庫的處理性能,是EverDB基于中間件對執行計劃一種特有的實現方式。
EverDB執行計劃不管從底層數據節點還是中間層,SQL優化算法方面,還有很多值得優化改進的地方。未來,EverDB會持續精進自身的各項能力,努力成為更出色的國產分布式數據庫產品。?