Cobar提出的一種在分庫場景下對Order By / Limit 的優化

捉蟲大師發表於2021-10-12

搜尋關注微信公眾號"捉蟲大師",後端技術分享,架構設計、效能優化、原始碼閱讀、問題排查、踩坑實踐。
本文已收錄 https://github.com/lkxiaolou/lkxiaolou 歡迎star。

Cobar 雖然是一款“古老”的資料庫中介軟體,但目前不少公司仍然在用它,且它包含了不少有意思的演算法和實現,今天就來分享 Cobar 提出的一種在分庫場景下對 Order By / Limit 的優化。

原演算法描述參考: https://github.com/alibaba/cobar/blob/master/doc/cobarSolution.ppt

背景

Cobar 最重要的功能就是分庫分表,通常讀取效能瓶頸可以通過增加從庫或快取來解決。

但寫入效能在 MySQL 上只能通過分庫分表來提升。

當我們把資料分佈到不同的資料庫上時,再查詢時如果是單條資料只要找到這條資料對應的庫即可,但如果是多條資料,可能分佈在不同的庫上時,Cobar 就需要先查詢,再聚合。
image

來個具體例子:

image

如果我們要查詢 tb1 表的 c1 欄位,且取 c1 正序的下標(從0開始)為4、5的資料。假設分了三個庫,我們為了取到正確資料,需要去這三個分庫都取下標0-5的資料,假設取到如下資料:

image

取到3堆已排序的資料,對這3堆資料從小開始丟棄0、1、2、3號資料,保留第4、5號資料即是我們需要的。

image

這個演算法看起來沒啥問題,但如果資料量稍微變化一下,比如:

select c1 from tb1 order by c1 limit 9999999, 4

如果還按照上述的方法來做,首先得去每個分庫查詢 0 - 10000003的資料,然後再合併丟棄0-9999998號資料。

相當於丟棄了大約不分庫時3倍的資料。這多少顯得有點浪費了。

演算法優化

  • Step1:將這條語句拆分成3條語句發給3個分庫:

image

  • Step2:找出查詢結果的最大和最小值,這裡假設最小值為3,最大值為11

image

  • Step3:以最小值和最大值為條件再次查詢

image

假設我們取得的資料如圖,那麼我們是不是很容易推斷出這些結果之前還有多少資料?

  • Step4:反查出每一個返回結果的 offset,這裡我們就能推斷出分庫1在最小值之前還有3333332條資料,分庫2在最小值之前還有3333333條資料,分庫3在最小值之前還有3333331條資料

image

這時,我們就可以丟棄合併後的0-9999998號資料了,分庫1、2、3將最小值之前的資料都丟棄共丟棄了0-9999995號資料,再丟棄3個最小值3剛好夠到了9999998,所以9999999號資料開始依次是4、5、5、6

image

演算法分析

效率

以上例來說明,未優化前:

  • 1次查詢,查詢的資料總量大約 3kw,丟棄9999999條資料

優化後:

  • 第1次查詢,查詢資料總量約 1kw
  • 第2次查詢,資料總量17
  • 丟棄3條資料

從這個例子可以看出,查詢的資料量大大減少,需要計算丟棄的量也大大減少

非理想情況

可能大家能看出來,上述例子是非常理想的情況,如果資料沒這麼“理想”,結局又是怎樣?

  • Step4 中反查的最小值之前不夠丟棄怎麼辦,比如:

image

  • Step4 中反查的最小值之前的資料比需要丟棄的資料多怎麼辦?

image

可以看出,如果是這兩種情況,這種演算法就沒法再次生效了。

優化的前提

根據上述兩種情況來看,可以總結出該演算法生效的前提是:

資料(排序欄位)在各個分庫上的分佈要均勻

其實可以做個極端的假設,比如只有第一個分庫上有資料,其他資料庫沒有資料,那麼這個演算法就失效了

總結

這麼來看,這個演算法是不是很廢?確實比較廢,就連 Cobar 中也沒有使用。

但在某些場景下還是有比較大的提升的,分庫的資料大部分時候是按欄位進行取模,所以可以認為幾乎是分佈均勻的,此時如果 Order By / Limit 是比較深度翻頁的資料,可以採取此策略,但也要進行兜底,如果返回的資料不滿足條件,繼續退化為最初的演算法,所以單次效率可能不高,但從統計值上來看其效率可能是更高的。


搜尋關注微信公眾號"捉蟲大師",後端技術分享,架構設計、效能優化、原始碼閱讀、問題排查、踩坑實踐。

image

相關文章