天天看點

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

在首屆阿裡巴巴中間件峰會上,來自阿裡巴巴drds團隊的夢實分享了《分布式資料庫——從線性擴充談分布式join》。他主要從oltp資料庫的線性擴充、水準擴容、in查詢、分布式join四個方面進行了分享。在分享中,他主要通過買家與訂單場景、家庭與孩子場景介紹了in查詢,通過同次元的join、廣播表的join、nested

loop join詳細介紹了分布式join的坑與填坑。

以下内容根據直播視訊整理而成。

在資料庫的使用過程中,我們難免會問到這樣的問題,為什麼分庫分表?答案是為了達到線性擴充。在本次分享中,我們能夠知道分布式資料庫中線性擴充的含義,學會判定一個系統與查詢能否達到線性擴充的目的,達到使用分布式資料庫的目标。

資料庫主要有兩類:olap資料庫,sql一般比較複雜,執行時間可能在秒級至分鐘級,響應時間越快越好(單sql占據更多的資源,例如map reduce模型),提供盡可能高的并發度;oltp資料庫,sql一般比較簡單,執行時間一般在毫秒級,響應時間在可接受範圍(例如10ms)内即可(單sql一般隻有一個線程執行),提供盡可能高系統容量。

對于oltp,來說,是否機器越多,sql執行越快?答案是否定的。對于oltp資料庫中的線性擴充,增加機器數,單sql的響應時間基本不會發生太大變化;增加機器數,能線性增加整個系統的容量(并發度、吞吐量、tps)。并且,<b>在資源一定的情況下,從單機到分布式并不能帶來更高的系統容量。</b>

比如做一個全表的count(*)操作,每個分庫上的時間可能是10ms,如果不帶拆分鍵的話則需要到所有的表上去執行一個count(*)操作,如果将這些sql并行的發下去,則會發現查詢也隻消耗了10ms的時間,與隻在一個分庫上執行的時間差不多。那為什麼還要帶拆分鍵?

比如我們有兩台db執行個體,如果帶拆分鍵的話,往分布式資料庫裡面送出一個查詢,到db這邊也是一個查詢,那麼送出6條查詢之後,系統容量是6qps。我們把送出到分布式資料庫中的查詢稱為邏輯查詢,把分布式資料庫到底層所執行的查詢稱為實體查詢。對于業務來說,分布式資料庫是相對透明的,它關注的容量指的是邏輯qps。

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

發現系統容量不夠時,需要進行擴容,我們需要加1台db,成本提升了50%。如圖所示,性能确實提高了50%。

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

假如我們的sql沒有帶拆分鍵,那麼它需要路由到所有的分片上去執行。一條邏輯sql會生成2條實體sql。擴容之後,成本提升了50%,一條邏輯sql會生成3條實體sql,系統性能也沒有變化。是以得出一個結論:系統一定要帶拆分鍵,否則沒有可擴充性。

具體場景是:提供一個買家的所有訂單id,查出這些訂單的資訊,訂單表按照訂單id進行拆分,select * from order where order_id in (?,?,?,?...),假設單db容量是1000qps。

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

比如,2005年資料分片數為16,平均一個買家的訂單數:2,in查詢涉及的分片數:期望值接近2,系統容量:16*1000/2=8000。到了2015年,資料分片數為64,平均一個買家的訂單數:200,in查詢涉及的分片數:期望值接近64≈全表掃描,系統容量:64*1000/64=1000。在這種場景下,in查詢不具備線性擴充能力。

具體場景是:提供一個家庭的所有孩子id,查出這些孩子的資訊,孩子表按照孩子id進行拆分,select * from child where child_id in (?,?),假設單db容量是1000qps。跟前面的差別在于,訂單數會随着時間的推移飛速發展,但是孩子數不會随着時間發生太明顯的變化。

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

假設,2005年,資料分片數:16,平均一個家庭的孩子數:1-2,in查詢涉及的分片數:期望值接近2,系統容量:16*1000/2=8000。2015年,資料分片數:64,平均一個家庭的孩子數:1-2,in查詢涉及的分片數:期望值接近2,系統容量:64*1000/2=32000。在這種情況下,成本提升了50%,系統容量也提升了50%,in查詢實作了線性擴充。

對in查詢來說,必須滿足下述條件才能做到線性擴充:in的值的數目遠遠小于分片數;一般情況下,in的值的數目在2-3個;in的值的數目不會随着業務的發展而增長。相反的,隻要有一條不滿足,那麼in查詢就無法做到線性擴充。

分布式join有很多種情況,主要分為兩大類:可下推的join,join操作由存儲完成,drds層針對join的結果進行處理,效率會高一些,因為存儲和計算在一起;不可以下推的join,存儲層隻做單表的查詢,drds完成join的操作。

<b>同次元的join</b>

可下推的join中很重要的是同次元的join,join的兩個表是按照拆分鍵做的join,簡單例子如下:

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

user與user_address需要join,并且均以user_id為拆分鍵。這樣的結果是,對于同一個使用者,其位址與它在同一個分片内。是以隻需要在存儲層把join做好就可以了。對于drds來說,這個join操作由下面的mysql或者rds來完成。drds層隻需要把join結果傳回去就可以了。這種join線性判斷的标準是與單表sql相同。

另外一個比較常用的join是廣播表的join。有些表具有以下特點:比較小,總會與其他表進行關聯,這時候就不适合将其放在其中一個庫上面,那麼,這個join就沒有辦法下推了。廣播表是指所有分片中都存在一個完整的副本,一般用于變更比較少,容量比較小,需要頻繁與其表發生關聯的表。一張拆分表join一張廣播表的簡單例子如下:

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

此時,選擇把level表作為廣播表複制到每一個分片上去,這樣的join也可以做下推,隻要做分片内的join就可以了。這樣一個查詢的線性判斷标準與單表sql相同,由拆分表上的查詢決定。

nested

loop join是不可下推的分布式join。具體例子如下:

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

user以user_id為拆分鍵,order(訂單)以order_id為拆分鍵。join不能由存儲來完成,隻能由drds層完成。具體的算法等價于在左表把需要的資料拿出來然後再去右表做應用查詢,即以小表驅動(經過where條件過濾後資料量較少的表為小表)。

對于這種join,線性判斷标準直覺來說是對兩個表的查詢均需要能夠線性。即對驅動表的判斷與單表查詢相同;對被驅動表的判斷:被驅動表的join列是否是拆分鍵,被驅動表做的in查詢的數目。

主要有兩張表,order表拆分鍵為order_id,user表拆分鍵為user_id。

<b>select* from order join</b>

user on order.buyer_id = user.user_id

結果分析:兩張表均是全表掃描。

user on order.buyer_id = user.user_id where order.id = 1

結果分析:order為驅動表,user表的join列是拆分鍵,一個order隻有唯一一個user。

<b>select* from user join</b>

order on user.user_id = order.buyer_id where user.id = 1

結果分析:user為驅動表,并且帶了拆分鍵上的等值條件,order表的join列不是拆分鍵,對于order表是全表掃描。

<b>select* from user_oders</b>

join order on user_oders.order_id = order.order_id where user_oders.user_id = 1

結果分析:user_orders記錄了一個使用者有哪些訂單,拆分鍵為user_id,user_oders表為驅動表,并且帶了拆分鍵上的等值條件,order表的join列是拆分鍵,一個user對應的order會随業務的增長而增長,在order表做的in查詢也會增長。

具體場景是:一個訂單,包含買家id(buyer_id)與賣家id(seller_id),買家希望根據買家來查,而賣家則希望根據賣家來查。那麼一張表通過買家來拆還是通過賣家來拆?

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

傳統的解法是空間換時間,模仿單機資料庫中建索引的這種方式,将資料存兩份,一份按買家id拆分,一份按照賣家id拆分。這個方案的缺點是占的空間太大,因為把整個表都備援起來了。

分布式資料庫——從線性擴充談分布式JOIN OLTP資料庫的線性擴充 水準擴容 IN查詢 分布式JOIN買賣家問題DRDS

或許有人認為,一種較好的解決方案是隻備援id列,隻需要買家id到賣家id的映射關系和賣家id到買家id的映射關系。在join查詢時,随着時間發展,一個買家對應的賣家會越來越多,那麼這就是一個錯誤的方案。更好的方案是把關聯關系做一個全備援,使其避免出現這樣的join查詢,用更多的空間來換取時間、線性擴充的能力。

如何快速判斷一個正在使用drds的系統是否能做到線性擴充?在drds監控頁面裡面有兩個監控名額,一個是實體qps,一個是邏輯qps。判斷的方法是,看這兩個名額是否接近1:1。如果接近1:1說明系統很大機率是可以線性擴充的。若遠遠大于1:1,那麼就不能線性擴充。