分散式資料庫排序及優化

vivo網際網路技術發表於2022-03-07

一、背景

1.1 分散式資料庫架構

當前分散式資料庫架構有不少,但是總體架構相差不大,主要元件都包含協調節點、資料分片、後設資料節點、全域性時鐘。一種常見的分散式架構如下圖:

圖片

  • gtm :全域性事務管理器(全域性時鐘),一主多備;
  • catalog: 後設資料管理,一主多備;
  • group: 水平分片,每個group由一主多備資料儲存節點組成;
  • proxy : 協調節點,無狀態,負責處理客戶端的請求,把請求按照分片規則傳送到資料分片,彙總資料分片返回的資料,協同其它元件保證分散式事務的一致性。

1.2 排序問題

分散式資料庫中排序也是一種重要的功能。一條查詢排序語句select *from t1 order by field1,需要查詢的資料可能會分佈在不同的資料分片中。這就需要proxy對為不同資料分片返回的有序資料進行重排序,然後後給client返回全域性有序的資料。

當相關的資料量不大時,proxy可把不同資料分片返回的資料儲存在記憶體中,然後對記憶體中的資料重排序後返回給client。當相關的資料量比較大時,如果把待重排序資料放到記憶體中則可能會導致OOM,如果把待重排序資料暫存在proxy的磁碟中,則也有耗盡磁碟的風險並且會存在大量的磁碟IO。下面將介紹一種分散式資料庫排序及優化方法。

二、解決方案

2.1 排序方案介紹

為了提高分散式排序的效能,每個資料分片本身也要參與排序。這樣在proxy上得到分片返回的資料是有序的,proxy對有序的資料重排序可以採用歸併排序或者優先順序佇列排序方法,大大減輕proxy的壓力。

可以根據proxy記憶體大小配置sort buffer大小,通常預設為10M。如果一次查詢語句關聯N個資料分片,則需要到sort buffer按照N份進行切分,每個資料分片對應切分後的sort buffer大小為10M/N。

直接在記憶體中進行,具體步驟如下圖:

圖片

  • client向proxy下發排序查詢語句 select *from t1 order by id。
  • proxy根據分片鍵以及分片規則向相關的資料分片group1、group2下發排序查詢語句select *from t1 order by id。
  • 資料分片在本地對資料進行查詢排序後,傳送有序資料到proxy。
  • proxy把資料分片返回的有序資料儲存在資料分片對應的sort buffer中,並對有序資料進行歸併排序。
  • proxy把歸併排序好的資料傳送給client。

2.2 排序方案缺陷

這種方法只能滿足小資料量排序,當排序的資料量較大我們可以選擇調大proxy上的sort buffer。但是調大sort buffer會佔用更多的記憶體資源,所以不能無限制的調大sort  buffer。

2.3 排序優化思路

把資料分片返回的有序資料儲存到磁碟上,然後對磁碟資料進行重排序。下面將介紹一種優化方案,針對大資料量進行分散式排序的方法。

三、優化方案

3.1 排序方案介紹

由於記憶體的限制,在記憶體中對大資料量資料進行歸併排序方案不可行,針對這種情況需要把資料分片返回的資料暫存在磁碟中。具體優化方案步驟如下圖:

圖片

1)client向proxy下發排序查詢語句 select *from t1 order by id。

2)proxy根據分片鍵向相關的資料分片group1、group2下發排序查詢語句select *from t1 order by id。

3)資料分片在本地對資料進行查詢排序後,傳送有序資料到proxy。

4)proxy把資料分片返回的有序資料儲存在資料分片對應的磁碟檔案中。

5)使用優先順序佇列排序方法進行重排序:

  • 每個資料分片出一條資料構建堆,heap包含的節點個數等於資料分片的個數。
  • 為了避免優先順序佇列排序過程中從磁碟中逐條讀取資料造成的效能問題,proxy從磁碟檔案中讀取資料預填充到資料分片對應的sort buffer。
  • 每個分片的sort buffer出一條資料構造成一個heap。
  • 從堆頂彈出資料傳送給client。
  • 堆頂資料彈出後,從已彈出節點對應的sort buffer再讀取一條資料push到堆。
  • 分片sort buffer中的資料取完後,需要繼續從對應的磁碟檔案中拉取資料,對sort buffer進行填充。
  • 直至取完所有資料傳送到client。

3.2 排序方案缺陷

  • proxy需要收集完所有相關資料分片的有序資料存入磁碟可以解決記憶體不夠的問題,但是磁碟也是有限的,當資料量太大在proxy上磁碟也可能無法容納需要排序的資料。
  • proxy上把資料存在磁碟,存在大量的磁碟IO。
  • 以select from t1 order by field1 limit 100w為例:如果本次查詢的資料在50個資料分片上,則proxy節點需要從每個資料分片上拉取100w資料然後儲存到磁碟上。這樣需要儲存5000W資料(100w50),而client只需要100w條資料,浪費了很多網路頻寬和磁碟IO。

3.3 排序優化思路

這種方法是proxy把相關資料分片的有序資料全部拉取到proxy上,然後再進行排序。我們是否分批從資料分片拉取資料,批量資料處理後再從資料分片拉取下一批資料呢?下面將介紹一種分批排序的方法。

四、最終方案

4.1 排序方案介紹

proxy上磁碟上不儲存資料分片資料,一次從資料分片拉取固定大小的有序資料,proxy把拉取的資料填充到分片對應的sort buffer,sort buffer中資料使用完後再次從對應的資料分片上拉取。具體步驟如下圖:

1)client向proxy下發排序查詢語句 select *from t1 order by id。

2)proxy根據分片鍵向相關的資料分片group1、group2下發排序查詢語句select *from t1 order by id。

3)資料分片在本地對資料進行查詢排序後,傳送固定大小有序資料到proxy。

4)proxy把資料分片返回的有序資料儲存在資料分片對應的sort buffer中。

5)優先順序佇列排序。

  • 每個資料分片對應的sort buffer出一條資料構建堆,堆節點的個數等於資料分片的個數.
  • 從堆頂彈出資料傳送給client.
  • 堆頂資料彈出後,從已彈出節點對應的sort buffer再讀取一條資料push到堆.
  • 分片sort buffer中的資料取完後,需要繼續從對應的資料分片節點中拉取資料,對sort buffer進行填充.
  • 直至取完所有資料傳送到client.

4.2 排序方案分析

針對優化方案3.2存在的三個缺陷的解決情況。

缺陷1:proxy需要收集完所有相關資料分片的有序資料存入磁碟可以解決記憶體不夠的問題,但是磁碟也是有限的,當資料量太大在proxy上磁碟也可能無法容納需要排序的資料。

解決情況:從圖中可以看出proxy的磁碟上不儲存資料分片的資料。

缺陷2 :proxy上把資料存在磁碟,存在大量的磁碟IO。

解決情況:proxy的磁碟上不儲存資料分片的資料,所以不存在磁碟壓力太大問題。

缺陷3:select from t1 order by field1 limit 100w為例:如果本次查詢的資料在50個資料分片上,則proxy節點需要從每個資料分片上拉取100w資料然後儲存到磁碟上,需要儲存5000W資料(100w50),而client只需要100w條資料,浪費了很多網路頻寬和磁碟IO。

解決情況:每次從資料分片拉取固定大小的資料,邊排序邊給客戶端返回資料,當給客戶端返回的資料達到100W時則完成本次查詢,網路頻寬浪費得到大大改善。

假設proxy上資料分片對應的sort buffer大小為2M,從資料分片拉取的資料量:

  • 最壞情況:拉取的資料量為 2M*50+100W,並且不需要儲存磁碟。
  • 最好情況:資料分佈很均勻,給client返回100w資料後,所有sort buffer分片對應的資料正好基本取空(都剩下一條),此時拉取的資料量為 100W+50。

4.3 方案使用限制

1)資料分片節點本身支援排序,絕大多數資料分片都是支援排序的。

2)資料分片需要支援分批讀取。

以MySQL作為資料分片為例,則需要 proxy上可以使用流式查詢或者遊標查詢。另外有些分散式資料庫在設計時就考慮到一些分散式的問題,它們資料分片節點在查詢結束前一直保留上下文,它們的分批讀取效能更高,這裡就不在舉例。

五、參考文獻

1.JDBC操作MySQL(3)—查詢

2.MySQL JDBC StreamResult通訊原理淺析

作者:vivo網際網路資料庫團隊-Xia Qianyong

相關文章