PolarDB-X技術解讀:Join the Dots of PolarDB-X 2.0

作者:吳學強

我們或許都玩過 Join the Dots 這個遊戲,一張紙上隨機的畫了很多點,每個點旁邊標了一個數字,沿着這些數字從小到大將這些點連接起來,帶着期待,我們發現了這些點構成的神奇圖案。對了,這個遊戲還有另外一個名字:Connecting the Dots ,或許你對它更熟悉^^。

過去的兩年,我們團隊花了巨大的精力對 PolarDB-X 進行了全新的設計,像這個遊戲的設計者一樣,精心思考每個“點”應該放在什麼位置。

2020年 5 月份,基於 PolarDB-X 1.0 近五年的打磨經驗和用戶反饋,我們重新設計了系統架構。新架構採用存儲計算分離方式,通過將計算節點與存儲節點進行一比一配比,簡化系統拓撲結構,存儲層採用基於 Paxos 協議的 X-DB,提供數據的強一致保障。計算節點與存儲節點通過私有協議邁出了突破性的一步,使得我們的分佈式事務、優化器等能力取得極大突破,產品上推出了更友好的一體化形態的 PolarDB-X 2.0。

9月份的雲棲大會上,我們推出了具備存儲資源強隔離、數據強一致的在線分析能力,開始往 HTAP 方向邁出第一步。同時支持了GSI(全局二級索引),使得 PolarDB-X 具備了單機數據庫一樣的索引能力。

接下來我們一起看下 PolarDB-X 2.0 又新增了哪些“點”,看看連接上這些“點”後能否更接近它的全貌。

HTAP

計算資源隔離

雲棲發佈的 HTAP 能力中,我們利用 Paxos 副本提供了存儲層資源的強隔離,對計算層資源僅做了幾個關鍵部分的隔離。本次發佈的版本中,我們對計算層的資源進行了系統化的梳理和設計,從而實現了計算層資源的強隔離。

CPU 隔離
PolarDB-X 計算層開發語言爲 Java,所以在 CPU 隔離上我們使用了JVM 相關的隔離技術。阿里JDK提供了基於cgroup、wisp/wisp2等 CPU 相關隔離方式,其中 cgroup 是以租戶形式提供強隔離,可以嚴格限制住 CPU quota。wisp/wisp2是基於協程時間片統計的軟隔離。通過cgroup attach線程有一定開銷,所以適合常駐線程池,wisp方式 attach 代價比較小,但wisp2 要求所有線程池都採用 wisp2。綜合考慮後,我們採用了cgroup + wisp結合的方式進行 TP 和 AP 的 CPU 資源隔離。

另外,對於 Parser、Optimizer 等混用的線程,我們引入令牌桶的方式更精確的區分 TP 和 AP 的 CPU 使用量。同時設計了 TP 退化成 AP 的策略,當一條 Query 執行時間超過 TP 閾值時,會退化成 AP 來執行,避免阻塞 TP 隊列,同時處理完畢後,會將該執行計劃反饋給 SPM,以便下次優化器以 AP 方式來執行相同請求。

Memory 隔離
PolarDB-X 2.0 通過 Memory Pool 機制來管理內存,Memory Pool 的目的是通過限制請求在各個階段臨時表的大小,保護計算節點的穩定運行。

Memory Pool 自頂向下分爲多個層次:

  • Global Pool:整個計算節點可以使用的臨時表內存,默認限制 = JVM_Heap_Size - 4GB
  • Schema Pool:單個 Schema 可以使用的臨時表內存,默認不限制
  • TP/AP Pool: 對 Schema Pool 做的一個邏輯隔離,其中 TP 與 AP 的 Memory 比例可以動態調整
  • Request Pool:單個 Query 可以使用的臨時表內存,以 TraceID 命名,默認限制 = Global_Pool_Limit / 4
  • Operator Pool:單個算子的內存,僅僅用於顯示、方便監控,默認不限制

計算節點在處理 Aggragate、Join、Sort 等算子時,會採用“臨時表”進行排序等計算,臨時表會通過 Memory Pool 進行管理。考慮到對象的序列化/反序列化的開銷比較大,當前內存佔用大小通過計算對象佔用空間實現,由於臨時表中的對象都是 Integer、String、Timestamp 等簡單對象,這種估算方式還是比較準確的。

當一個請求中用到臨時表時,會從 Memory Pool 中扣除其空間佔用,臨時表釋放後,該空間會還給 Memory Pool。

若 Memory Pool 超過其大小限制,空間扣除請求會失敗,並返回異常。

另外,我們還設計了 Memory Pool Reset 機制,用來應對“泄露”等內存管理異常情況。

對於 Schema Pool 裏的內存資源,在 HTAP 場景下,首先需要保證 TP 請求的高優先級使用,其次應該最大化 AP 請求的資源可用量。所以我們對 Memory Pool 做了彈性劃分,分別爲 TP 和 AP 設置了高低水位。默認情況下,TP 內存水位比例爲 20%-80%,AP 內存水位比例爲 30% - 60% 。當內存使用衝突時,優先保障 TP 請求。比如當前 TP 水位 50 %,AP 水位 45%,當一個 TP 請求內存使用量超過 5% 時,會觸發系統 kill 機制,該機制會終止 AP 正在處理的內存使用量排名第一的請求,釋放內存供 TP 請求使用。

Memory Pool 的保護與隔離機制,即保證了計算節點的穩定運行,又使 TP 和 AP 內存資源使用上保持了足夠的彈性。

MPP Cluster 資源調度
CPU 資源隔離與 Memory Pool 機制都是單個計算節點上的資源隔離方式,HTAP 中的 MPP 能力會把所有計算節點當成一個整體進行任務調度,所以每個計算節點負載的相對均衡,也是系統總體穩定的一個關鍵因素。

基於這個考慮,MPP 在進行任務調度時,會遵循以下幾個原則:

  • 主實例可以向主實例和只讀實例所有計算節點派發任務,只讀實例只能向所有隻讀節點派發任務
  • 在派發任務時,若某個計算節點負載超過閾值,則不向該節點派發任務
  • 若可用節點數少於所需節點數,退化爲 Local 模式在本節點完成計算
  • 若派發任務時,實例正在版本升級,首先評估兼容性後,再決定是否派發

下圖是我們進行的一次 TPC-C 和 TPC-H 混合負載測試。第一階段爲 TPC-C 測試,可以看出此時系統吞吐保持在一個穩定的水位。第二階段加入 TPC-H 負載,但不做 TP 與 AP 隔離,此時系統進入一個劇烈波動狀態,雖然TPC-C 和 TPC-H 可以繼續執行,但相互干擾非常嚴重。第三階段動態開啓隔離後,系統恢復到一個穩定的狀態,此時 TPC-C 正常進行,TPC-H 也能在短時間內跑完。

RuntimeFilter

RuntimeFilter 一般是 MPP 數據庫對於 Join 的一種優化手段,可以減少數據讀取,減少 Shuffle 數據量,對於可以命中的查詢,性能可以有非常大的提升。比如對於下圖中的 SQL,我們在 Join 構建 Build 表過程中,基於items.id 構建一個 BloomFilter,然後在運行過程中將 BloomFilter 動態推給 Probe Scan,這樣在 Shuffle 之前經過 BloomFilter 提前過濾,可以大幅減少 Shuffle 數據量,提高性能。

RuntimeFilter 業界有多種實現方案,Impala 通過 Coordinator 在 Shuffle 階段均可支持 RuntimeFilter,Presto 的方案相對有侷限性,僅支持右表可以 broadcsat 的場景,Flink 的方案與 Impala 類似,都是通過中央化的 Coordinator 做 BloomFilter 的 merge 後再廣播,不同點是 Flink 會更多的考慮 BloomFilter 的下推。

PolarDB-X 採用類似 Flink 的方案,即通過 Coordinator 完成 BloomFilter 的 merge 和廣播,並儘量考慮 BloomFilter 的下推,在此基礎上,我們會利用優化器的統計信息進行代價評估,進一步判斷 RuntimeFilter 是否值得去做。另外,我們的存儲層是 MySQL,所以 BloomFilter 可以直接下推到 MySQL 進行計算,這樣可以大大減少 Scan 返回的數據量,進而降低網絡開銷。

我們對 RuntimeFilter 特性開啓前後進行了 TPC-H 性能測試,結果如下圖。

從測試結果來看,RuntimeFilter 對 Q5、Q8、Q9、Q17、Q21 等多條 Query 有非常明顯的加速作用,對大部分 Query 都有提升,總體性能提升在 20% 以上。

另外測試結果同時可以看出,有部分 Query 出現了少量的性能回退,分析原因後我們認爲,這主要是 CBO 採用的統計信息不夠準確導致的,目前我們在估算過濾率時採用的算法是

,這是個很粗糙的方式,其假設數據是均勻分佈的,所以目前我們正在嘗試跟 CBO 有更好的結合以便做進一步的優化。另一個優化點方向是,對於星型模型和雪花模型的查詢( PKFK )的查詢,在 RuntimeFilter 生成階段仍然可以進行 Join Reorder,其複雜度爲線性,我們評估後認爲會對 TPC-DS 等更復雜查詢有很大幫助。

除了計算層資源隔離、RuntimeFilter 等優化或特性,2.0 中還增加了LookUpJoin 流式執行、Window Function等,這裏不再展開。

透明分佈式

兼容性

PolarDB-X 作爲一個新進者,保持與現有數據庫生態的兼容性是我們長期追求的一個目標。本次發佈的版本在 SQL 語法體驗上有了質的改變。

如上圖所示,在建表時只需通過有無 'PARTITION' 關鍵字區分單表和拆分表,無需通過其他語句額外指定拆分鍵、拆分算法、分片數量等,極大簡化了分佈式上手門檻,熟悉 PolarDB-X 1.0(DRDS) 的用戶應該有更加強烈的體感。另外,用戶可以像使用單機索引一樣,使用 GSI(全局二級索引)對查詢進行加速,其語法跟單機完全一致。

這些簡單的語法改動的背後,是對系統重新設計的結果。

在拆分策略上,系統會默認選擇主鍵進行拆分,對於沒有主鍵的表,系統會增加隱藏主鍵。拆分算法默認選擇一致性Hash 算法,減少分片數變化時需要搬遷的數據規模,提高拆分效率。

索引創建上,默認會選擇 GSI,GSI 與主表數據通過分佈式事務保持強一致。在處理請求時,優化器會根據優化規則和統計信息選擇合適的 GSI,以提高查詢效率。

同時,對於高級用戶,我們提供了設定拆分算法等入口,同時也提供了GSI 與 Local Index 的設定入口。

SQL 限流

實際業務中我們經常遇到數量不多但非常耗資源的 SQL 把整個體統拖慢的情況,或出於業務或管理要求,需要對某類 SQL 進行臨時限制,我們提供了 SQL 限流功能以滿足這部分訴求。

對於限流或限制資源使用,目前業界有多種實現方案,Oracle 通過設置用戶級別的資源(CPU、Memory、IO)使用量進行限制。SQL Server 通過將 Workload 按照指定規則分類的方式進行資源(CPU、Memory、IO)隔離,並且可以實現類似多租戶的隔離效果。阿里雲 RDS for MySQL 通過存儲過程實現對限流規則的管理。

PolarDB-X 中提供基於規則匹配的 SQL 限流方式,語法如下。

CREATE CCL_RULE IF NOT EXISTS ccl_name  ON database_name.table_name TO ‘username’@’host’
FOR { UPDATE | SELECT | INSERT | DELETE }
[filter_options]
with_options

user:
  ‘username’ @ ’host’
filter_options:
    [ FILTER  BY KEYWORD(‘KEYWORD1’, ’KEYWORD2’,…) ]
    [ FILTER  BY TEMPLATE(‘template_id’) ]

with_options:
    WITH MAX_CONCURRENCY = value1 [, WAIT_QUEUE_SIZE = value2, WAIT_TIMEOUT = value3]
     
DROP CCL_RULE id1;
CLEAR CLL_RULES;
SHOW CLL_RULES;

注:左右滑動閱覽

該限流方案可以通過庫名、賬號、併發數、關鍵字、等待數量等多個維度設置限流規則,限流規則實時生效。

在高併發場景下,限流規則帶來的 overhead 是需要重點考慮的因素。經過測試,我們發現限流的 overhead 主要在 SQL 解析上,所以通過將 SQL 轉換成模板並緩存,我們將限流的 overhead 降低到可以忽略不計的程度。

下圖是 SQL 限流功能的測試示例。測試中,我們用 TPC-C 模擬業務流量,中間用 JMeter 跑全表掃描這樣的耗資源 SQL,之後通過動態添加規則對其進行限制。

限流的半自動甚至自動化是我們追求的下一個目標,該能力會通過收集 Query RT、CPU quota、Memory 使用量等多維度信息,並配合用戶設置的限流策略實現。

SQL Advisor

實際業務中如果發現慢 SQL 並嘗試通過添加索引來加速查詢,添加索引時,DBA 或內核研發同學會依賴 SQL 所涉及的表的數據量(統計信息),過濾條件,Sort、Join、Aggragate 方式等等來考慮建議添加什麼索引。之所以知道添加某些索引有效是因爲這些同學對數據庫的優化執行行爲有比較深刻的認識。

PolarDB-X 2.0 中,我們參考微軟在 VLDB 發表的一篇論文中的方法實現了 SQL Advisor 能力。

具體來說,SQL 中存在一些所謂的 Indexable Column,比如 Where 條件中的列。這些列通過單個或多個組合構成 Candicate Index,而多個 Candicate Index 可以組成一個 Configuration。我們的目標就是通過優化器對這些 Configuration 進行代價評估,選出最優的結果並推薦給用戶。

實現中,我們用過將 SQL 解析並通過 RBO 優化的邏輯計劃做 Visitor + MetaQuery 遍歷找到 Indexable Column。之後採用啓發式規則構造 Candicate Index。根據經驗,一般一個索引的前兩列提供了最大貢獻,並考慮到 DML 負擔和死鎖風險,所以我們選擇兩個列組成索引。Configuration 構造上則直接通過枚舉所有的 2 列組合索引完成,即總數爲 C(n,2)。

在 Configuration 對比階段,考慮兩個 Configuration A 和 B,我們基於以下原則進行選擇:

  • 當使用 A 後,SQL 的代價比使用 B 更低,超過一定閾值則認爲 A 更好
  • 當 A 與 B 的代價差別不大,如 1% 範圍。則索引越少的越優,索引數相同情況下,索引總長度越少越優

下面是我們的一次測試的截圖。啓用 SQL Advisor 做索引推薦前,一條 SQL 的查詢時間爲 28.76s。

之後查看 SQL Advisor 給出的索引推薦並添加相應的索引。

添加索引後執行相同的 SQL,時間減少到 1.41s。

PITR

備份恢復是數據庫的必備能力,分佈式數據庫的備份恢復在實現上相對於單機有更大的挑戰,PolarDB-X 2.0 中提供了一致性備份和任意時間點恢復能力。

備份階段,我們通過 TSO 獲取全局版本號,並用該版本後從存儲節點副本中獲取全量數據的一致性快照,同時會將 Binlog 日誌進行歸檔,Binlog 中帶有 TSO 版本號信息。

恢復階段,首先根據指定的時間點選擇最近的全量備份集進行恢復,之後根據該全量快照所對應的 TSO 版本號定位到 Binlog 中的起始位置,重放 Binlog 直到指定的時間點,這樣便完成了數據的任意時間點恢復。

一致性與高可用

智能並行發包

上圖展示的是一個部署了三個節點的存儲集羣,設計上引入了多分組 X-Paxos 技術替換傳統的複製協議,基於多分組技術可支持多點寫入+多點可讀的能力,解決了傳統 MySQL 數據庫下的單點寫瓶頸的問題。

多分組 Paxos 相比於單分組 Paxos 會帶來額外的資源開銷,所以我們通過節點上共享網絡通信模塊和幷包的方式解決了連接風暴和消息風暴問題,通過持久化 X-Paxos 分組和數據分片的映射關係,保持了 Consensus 層的獨立性和通用性,降低了系統耦合度。

異步化事務提交

傳統的 MySQL 都是 One Thread per Connection 的工作模式, 在引入線程池後是以一個線程池孵化一定數量的工作線程, 每個線程負責處理一次 Query 的解析、優化、修改數據、提交、回網絡包等等。在集羣跨地域部署場景下,一次事務提交由於需要在集羣之間同步日誌,網絡通信帶來的延遲會達到數十毫秒級別。如果將整個事務的提交異步化,將工作線程從等待 X-Paxos 日誌同步中解放出來,在大負載下可以擁有更高的處理能力。

異步化提交核心思想是將每個事務的請求分成兩個階段,提交之前一個階段,提交和回包一個階段。兩個階段都可以由不同的工作線程來完成。爲了完成異步化的改造,我們增加了等待同步隊列和等待提交隊列,用於存儲不同階段的事務。這樣一來,系統中只有少數的線程在等待日誌同步操作, 其餘的線程可以充分利用 CPU 處理客戶端的請求,異步化提交結合 MySQL 的 Group Commit 邏輯,讓 Worker 線程可以去執行新的請求響應。經測試,同城部署場景中異步化相比同步提交有 10% 的吞吐量提升,跨區域場景下有 500% 的吞吐量提升。

熱點更新

熱點更新原本就是數據庫的一個難題,PolarDB-X 在 AliSQL 的熱點功能之上優化了複製,使得在保證數據強一致的情況下,熱點更新性能提升非常明顯。

如上圖所示,針對熱點行更新的基本思路是合併多個事務對同一行的更新。爲了讓批量的更新事務能夠同時進行提交,PolarDB-X 在存儲引擎中增加了一種新的行鎖類型——熱點行鎖。熱點行鎖下,熱點更新的事務之間是相容的,爲了保證數據的一致性,對同一批的熱點更新事務日誌打上特殊 tag, X-Paxos 會根據 tag 將這一整批事務的日誌組成一個單獨的網絡包進行集羣間的數據同步,保證這些事務是原子的提交/回滾。除此之外爲了提升日誌回放的效率,PolarDB-X 將每個批次事務中對於熱點行的更新日誌也做了合併。

總結與展望

本次發佈的版本中,我們通過計算資源強隔離、RuntimeFilter、Window Function、LookupJoin流式執行等,提供了更穩定、更易用、更高效的 HTAP 能力。通過接近單機數據庫的建表語法以及系統性的設計,極大的降低了分佈式的上手門檻,同時提供拆分算法、局部索引等的設定入口,滿足高階用戶的需求。通過提供維度豐富、overhead 極小的 SQL 限流能力,滿足用戶對耗資源 SQL 實時限制或 SQL 分類管理訴求。通過設計索引組合和評估框架,SQL Advisor 將 CBO 的優化能力賦予用戶,讓索引選擇變得更加簡單。通過 TSO 版本號,將分佈式數據庫備份恢復這項有挑戰的功能做到了極致。存儲層,通過 Paxos 智能並行發包、事務異步提交優化、熱點寫入場景優化等,進一步提高了存儲的穩定性和效率。

這些功能“點”逐漸勾勒出了 PolarDB-X 2.0 的輪廓,我們依然在快速迭代,依然在精心打造接下來的每一個“點”:更強的優化器、更高效的分佈式事務、更優的 HTAP、更極致的彈性、更好的兼容性、更完備的容災能力、更豐富的企業特性。Join the dots,讓我們一起期待~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章