本文分享自華為雲社區《GaussDB(DWS)存儲引擎:從CU入手優化HStore表》,作者: yd_261437590。 1. 前言 適用版本:【8.2.1(及以上)】 HStore同時擁有處理傳統TP場景的事務能力和強大的數據分析能力,但是強大的數據分析能力很可能被小CU問題給破壞,另外,將多個 ...
本文分享自華為雲社區《GaussDB(DWS)存儲引擎:從CU入手優化HStore表》,作者: yd_261437590。
1. 前言
- 適用版本:【8.2.1(及以上)】
HStore同時擁有處理傳統TP場景的事務能力和強大的數據分析能力,但是強大的數據分析能力很可能被小CU問題給破壞,另外,將多個CU排序可以增加HStore的數據聚簇性,因此作者通過解決小CU問題和提升數據聚簇性兩種方式對HStore表的存取能力進行優化。
2. HStore簡介
2.1 行存儲
傳統OLTP(OnLine Transaction Processsing 聯機事務處理)場景與功能、業務強相關,數據需要進行頻繁的增刪改查,這時比較適合使用行存儲式。行存儲的優勢主要有兩個方面:首先是點查性能好,在點查場景下可以直接索引到某行數據的元組位置;其次就是更新效率高,行存儲在實時併發入庫,併發更新方面依然有著比較大的優勢。
2.2 列存儲
傳統行存儲形式的資料庫主要為業務服務,但是如果涉及到分析查詢場景,特別是在數據量大且複雜的查詢時,就會遇到性能瓶頸了,性能瓶頸是數據存儲方式決定的。因此OLTP(OnLine Transaction Processsing 聯機事務處理)場景一般會交給列存儲引擎去做。列存儲的優勢主要有兩方面:首先是批量查詢性能好,當分析查詢只涉及某列或者某幾列,不需要訪問無關列,特別是在表的寬度比較大時(如一千列),優勢更加明顯;其次就是列存儲的壓縮性能更高,原因就是因為數據按列存儲,單列類型相同。
列存儲引擎的最小存儲單位是CU(Compression Unit, 壓縮單元):一個CU是由表中某一列的一部分數據組成的壓縮數據塊, 通過(cu_id,col_id)標識一個CU。
圖1 列存儲
另外,列存引擎通過delta表,避免了小CU的產生,顯著提升列存表單條導入的性能,同時解決由於小CU導致的數據膨脹問題。當單條或小批量數據導入到列存表時,需先存入delta表,當delta表中數據積攢到指定行數時再存入新產生的CU中。
2.3 HStore
列存儲優勢明顯,但是劣勢也比較明顯,傳統列存表基本無法支持併發更新入庫。隨著業務複雜程度的提升,出現了對於實時入庫和實時查詢有較強訴求的場景,這要求資料庫同時擁有處理傳統TP場景的事務能力和強大的數據分析能力。這時就可以使用HStore來處理這些場景了。
圖2 HStore存儲
HStore利用delta表存儲update/delete/insert等操作信息。之後依賴後臺常駐autovacuum來做merge操作將數據寫入主表。
HStore的Delta表與普通列存Delta表的對比
數倉類型 | 列存的delta表 | HStore的delta表 |
---|---|---|
表結構 | 與列存主表的表定義一致 | 與主表表定義不一樣。 |
功能 | 用於暫存小批量insert的數據,滿閾值後再merge到主表,避免直接insert到主表產生大量小CU。 | 用於持久化存儲update/delete/insert等操作信息。 |
缺陷 | 來不及merge導致delta表膨脹,影響查詢性能,同時無法解決併發update的鎖衝突問題 | 依賴後臺常駐autovacuum來做merge操作。 |
利用特有的delta表,HStore解決了傳統列存表CU鎖的問題,支持上游upsert/update等操作實時併發入庫。同時還能保證和普通列存表相近的數據分析與數據壓縮能力。
HStore表技術特點如下:
- 完整的事務一致性:支持全面的事務能力,數據插入或者更新提交後即可見不存在時延,保證數據ACID一致性。
- 全面的功能支持:提供和當前列存一樣全面的功能和語法支持。
- 查詢性能好:適用多表關聯等複雜AP查詢場景,相對於傳統行存表,擁有更完善的分散式查詢計劃與更先進的分散式執行器,性能優勢明顯。支持複雜的子查詢和存儲過程,支持主鍵等傳統索引能力去重和加速點查,也支持分區、全局字典、局部排序等方式進一步加速AP查詢。
- 入庫快:徹底解決列存CU鎖衝突問題,支持高併發的更新入庫操作,典型場景下,併發更新性能是之前的百倍以上。
- 高壓縮:數據在MERGE進入列存主表後,按列存儲具有天然的壓縮優勢,能極大地節省磁碟空間與IO資源。
3. 小CU問題
3.1 問題誘因
- 有些實時表入庫量並不大,不定期會有入庫,因為merge的判斷標準有兩個:行數或者時間,超過時間沒有入庫後也會強制merge,這種情況下merge產生的CU的行數不可控,可能產生小CU;
- 對於緩慢變化維表來說,可能很長時間才改變一次,每次都可能產生一個小CU,雖然不會有太多這種小CU,但長期運行後,這種維度表數量還很多的情況下,小CU的數量就會到達影響系統性能的級別;
- 頻繁upsert、update、delete等更新後,CU中大部分數據被標記刪除,這樣的CU雖然會被列存vacuum通過填充NULL進行回收,但是依然會導致小IO和cudesc表的膨脹,進而影響性能。
3.2 問題影響
- CUDesc並不會因為CU變小而變小,因此當小CU過多會導致存儲利用率過低。比如一個1000列的大寬表產生的CU只包含1行數據,但是因為每一列都會在CUDESC表中記錄,CUDesc也會增加一千多行數據;
- 只剩下幾十行甚至幾行的小CU會引發大量的小IO;
- 粗過濾效率降低,因為CUDESC表中會存儲CU的最值,當進行查詢時可以先通過最值進行粗過濾,但是如果CU中數據太少導致數據範圍小,則會降低粗過濾效率;
- 降低壓縮率。因為數據壓縮是以CU為單位的,但是CU過小會導致壓縮表現達不到預期
可以認為0 CU其實是小CU的一種特殊極端情況,0 CU相對非0的小CU對於性能影響小很多,因為0 CU只用載入deletemap。
圖3 CU管理
3.3 解決思路
3.3.1 小CU合併
小CU合併不是直接產生新的CU,而是將小CU數據重新插入到delta表後標記刪除,然後依賴delta表的自動merge攢夠後再產生完整的新CU;
圖4 小CU合併
圖5 小CU合併時單條數據的處理
小CU合併的事務可見性基於現有的csn機制,compaction inprogress或者回滾對外不可見,還是看到老記錄,compaction提交老記錄就不再可見看到新記錄。
具體一個CU中剩餘多少條數據才算是小CU,應該是與業務強相關。因此,小CU閾值應該可以使用GUC參數調節3.3.2 0CU清理
0CU的處理比小CU的處理簡單的多,我們直接從CUDESC表中將0CU記錄刪除即可。這裡指的刪除天然支持MVCC,因此老的快照查詢依然可以訪問被刪除的記錄。
小CU合併的過程就是不斷的嘗試把小於一定閾值的CU標記刪除,轉移數據到delta中,直到這個CU全部被標記刪除後變成0 CU,就可以當做0 CU徹底清理。3.3.3 效果
成功解決小CU問題,並且在小CU合併期間對實時入庫性能幾乎沒有影響(推薦小CU行數閾值下upsert性能劣化1%),但是因為小CU問題的解決,可以很好的解決查詢性能劣化,空間膨脹等問題,並且小CU合併完成後,最終實時入庫性能還是會有顯著提升。
4. 提升數據聚簇性
4.1 需求來源
在對HStore進行點查時,會首先通過CU的min/max來進行粗過濾,我們希望通過min/max過濾掉大部分數據,這就要求每個CU的數據儘可能的接近,而不能過於分散。目前GaussDB已經實現了局部聚簇 (Partial Cluster Key, 簡稱PCK),在數據批插過程中就會進行排序。但還是會有如下幾種情況導致CU的聚簇性無法達到要求:
- 寫入數據時,如果不是批量導入,則不會把數據寫入排序器,而是直接插入delta表,當delta表merge的時候,也不會先走排序邏輯,而是直接將數據寫入CU;
- 當CU中的數據被刪除的足夠多時,就變成了小CU,聚簇性本身就會變差,就算進行了小CU合併,也依然不會走排序邏輯,而是將數據直接寫入delta表,merge流程與1)一致;
- 實際上就是增加數據+刪除數據。
4.2 解決思路
通過將HStore中多個CU的數據根據partial cluster key進行排序,生成新的CU再重新寫入,新CU的數據會有更高的聚集性,即CU的min,max會在一個較小的區間內。非同步排序時的併發處理與小CU合併類似,見3.3.1。
圖6 非同步排序基本原理
4.3 效果
經過測試,排序後的CU聚簇性極大提升,粗過濾效率的提升與原本的數據特征有關。但是排序過程中會對所有參與排序的CU加CU級鎖,此過程會阻塞部分DML操作,因此不建議在業務高峰期使用此功能。
5. 總結
本文主要講解瞭如下幾個方面:
- 大致介紹了GaussDB實時數倉的重要解決方案:HStore;
- 引出小CU問題並給出瞭解決方案;
- 從數據的聚簇性作為切入點,提出非同步排序來優化HStore表的scan性能;
6. 參考文檔
數倉實時入庫利器:HStore表原理與應用實踐詳解。 作者:馬俊松(華為雲GaussDB(DWS) 技術佈道師)