大數據

正確設計Hologres實時數倉,性能提升10倍+

概要:天貓雙11對於零售通團隊來說也是全年最大的一場戰役,數據響應需要更實時,但也會相應增加更多的個性化指標,業務面臨的挑戰也會更大。本文將會講述阿里巴巴零售通數據平臺如何優化Hologres實時數倉,達到性能提升10倍+的效果,完美支撐雙11營銷活動、實時數據大屏等核心場景。也希望通過此文對Hologres新用戶起到一定的幫助作用,通過合理的數倉設計實現事半功倍的性能效果。

作者:
曹泰銘(瀟銘) 阿里巴巴零售通事業部高級數據工程師
汪宇(旋宇) 阿里巴巴零售通事業部高級數據工程師

背景

阿里巴巴零售通團隊是阿里巴巴B2B事業群針對線下零售小店(便利店/小超市)推出的一個為城市社區零售店提供訂貨、物流、營銷、增值服務等的互聯網一站式進貨平臺,實現互聯網對線下零售業的升級,同時也為有志於線上線下零售業的創業群體提供創業平臺。
整個平臺的架構圖如下所示,是一個流批分離且以離線為主的架構。零售通數據團隊負責對離線MaxCompute數倉和實時Flink數倉的建設和維護,對內部小二和外部生態夥伴提供決策支持,數據服務和數據產品建設。

0.png

因為Hologres與MaxCompute有著極好的兼容性,並且能夠對MaxCompute毫秒級加速,財年初零售通數據團隊作為業務數據中臺基於Hologres在業務上進行了大量的嘗試,包括實時數倉公共層,冷熱混合計算,查詢加速等場景,因為Hologres的加入,相同的需求量,以前的架構需要2天才能完成,而現在在2小時內就能完成,大大提升了開發效率,得到了研發們的一致好評

天貓雙11對於零售通團隊來說也是全年最大的一場戰役,數據響應需要更實時,但也會相應增加更多的個性化指標,業務面臨的挑戰也會更大。基於Hologres日常在業務的優秀表現,團隊也決定使用Hologres作為雙11核心開發產品,應用於雙11核心場景包括營銷活動中心,實時數據大屏等。

在10月份對全鏈路進行了幾次壓測,Hologres在100%壓測壓力下CPU和內存資源在100%線上徘徊,屬於壓線通過。本以為能順利通過大促考驗,但在2020-11-01這天,在大量查詢QPS和高併發的數據寫入峰值下,Hologres的RT延遲一度達到90秒,沒有達到預期。在Hologres團隊的緊急支持下,通過整體結構的調整以及相關性能調優,整體性能提升了10倍+,順利扛住2020-11-01的流量洪峰,同時通過此次調整也平穩的支持了整個雙11期間的流量洪峰(包括2020-11-11當天的波峰),為雙11劃下了圓滿句號。

事後我們針對整個事件做了全鏈路覆盤,發現主要問題還是出在對於Hologres的原理了解不夠深入,包括技術原理、Table Group、表結構設計等,導致某些用法還不是最優,這也導致在實際業務場景中,Hologres的性能沒有發揮到最大化。

我們將會通過此文講訴阿里巴巴零售通團隊如何根據業務場景合理的設計Hologres實時數倉,包括表結構、Table Group設計等,以到達更優的性能表現。也希望通過此文對Hologres新用戶起到一定的幫助作用,通過合理的資源利用實現事半功倍的性能效果。

11月1日現場還原

首先我們先來還原一下11月1日的現場反饋,以更直觀的方式來看看當時Hologres承受的壓力和相關表現(注:監控頁面為Hologres在阿里內部的監控頁面,與公有云的監控頁面和指標項略有不同):

查詢QPS: 每秒完成query的數量,一般代表數據庫性能和壓力,23:30後的增長代表壓力負荷增長(業務方開始大量關注/查詢報表),00:15分的驟降代表性能降低,1點之後的降低代表業務水位下降。

查詢延遲(RT):15分左右陡升到25s,大概持續15分鐘;Hologres超負荷運作,性能下降。
1.png

CPU使用率:代表正在運轉的core數量。我們的實例有幾百個core用於計算,但在峰值嚴重超負載50%+;

Woker CPU使用率:Woker的CPU負載情況,和CPU使用率基本一致,峰值超負載50%+。
這兩個指標代表大量Query將會無法及時處理,在隊列中等待,延遲將會較大增加,實例也有停擺的風險。

2.png

Blink寫入RPS:代表每秒實時數據的寫入量,0點是實時訂單寫入的峰值。當時有較多數據導入,對Worker產生較大的負荷,影響實例性能(比如RT,CPU使用率等),所以對部分blink寫入腳本採取緊急降級操作,峰值過後1:10恢復降級,產生第二波較大的寫入。
3.png

問題定位和優化手段

在Hologres團隊的幫助下,經過反覆的排查,發現問題主要有以下幾個原因:

1)主要問題: Table Group&Shards數設置不合理,在高併發讀取和寫入峰值時,造成集群資源浪費和性能下降。

零售通Hologres實例總計有幾百core的計算資源,幾TB的內存資源,和十幾TB的存儲資源。在實例創建後會根據實例規格生成默認數量的Table Group,然後每個Table Group會自動創建對應的Shard數(按照現有的規格,Shard數有300個),實例中存儲的表都會被默認分發至這些Shard中。

開發視角的Table Group和Shard

首先,先從開發者視角來理解一下Table Group和Shard的相關概念和原理:
在Hologres中,1個DB包含多個Table Group,每個Table Group包含多個Table,每個Table只能屬於一個Table Group。一個Table Group唯一對應一組Shard,由這組Shard來負責其中表的數據存儲和查詢,其包含的Shard個數稱為Shard Count,Table Group一旦建立,Shard數不可調整。

一個Table Group擁有的Shard數量(即Shard Count,後同)是它的一個重要屬性。Shard數多的Table Group,其數據寫入和查詢分析處理可以得到更大的並行度,一定範圍內,增大Shard數可以加快數據寫入和查詢分析的速度,但Shard數也並非越多越好,更多Shard數需要更多的節點間通信資源、計算資源以及內存資源,在資源不滿足的時候,或者Query很小時可能會導致適得其反的效果。

4.png

再結合具體的業務場景,當一個雙表Join的SQL執行時,按照現有Shard數,執行計劃會產生一個300*300 Shard的笛卡爾積(兩張表都被分散300份),這在shuffle階段對CPU和內存產生巨大的壓力,也就意味著需要更多的節點間通信資源、計算資源以及內存資源。除此之外,單表的數據隨機分散到300個Shard的過程中容易出現數據傾斜的問題,如下圖所示
5.png

設置Table Group和Shard建議

零售通團隊的業務場景大都是數據量偏少、表大小也非常不均勻,對於保障的優先級也不一致,所以上面將的所有表都放在一個300個Shard的Table Group中對實際業務並不適用,這就導致當流量增大時,沒有辦法有效利用Hologres的Local Join能力,導致CPU的開銷劇增。
正確的做法是按照使用場景、Join頻次、表大小,分裂設計成不同的Table Group中存放數據;一方面可以提升集群性能提高計算速度,另一方面可以節省資源同時一定程度上實現資源合理分配隔離。

新建Table Group的原則:

  • 數據量過大,可新建獨立的較大Shard數的Table Group。當察覺寫入性能變慢 or 讀取200萬行一個Shard時建議新建Table Group;
  • 有大量數據量很小的表,可適當獨立出一個小Shard數的Table Group,減小Query啟動開銷;
  • 需要Join的表,儘量放在同一個Table Group;

為Table Group設置合理的Shard數:Shard數不是越大越好,過大的Shard數會造成資源的浪費&負載過高,過小的Shards數會導致大數據量下讀寫性能不足以及不能抵擋較大的併發,下面也是經驗總結出來的Shard數設置(僅供參考,實際需要根據實例規格和業務要求來設置)

  • 查詢性能要求:若是業務對於查詢要求較高,Shard數的設置是表在SQL中掃描的分區範圍內的總行數的均值/200萬 = 1 Shard
  • 寫入性能需求:Shard數和數據寫入性能呈一定的正相關性,單個Shard的寫入能力是相對固定的,Shard越多,寫入的併發越多,寫入的吞吐越高。因此,如果表有較高RPS的寫入需求,需要增大Shard數。

具體完整的計算方法參考 如何選擇合適的Shard數,也可以諮詢Hologres技術小哥。

Table Group重構設計

針對Table Group的設計,零售通對當前Hologres實例的期待是作為分析型OLAP實時數據庫,在財年不擴充資源的情況下對現有表和業務需求梳理後有以下訴求:

  • 存放數倉中公共明細層,公共彙總層,維表;並能對公共層較明細的數據進行快速分析&開發(Local Join);表單分區一般在數千萬行量級
  • 營銷活動分析數據產品的接口層存儲,表格行數較少一般在單分區3000行以下,表格數量較少(10個以下)的Local Join,併發較高但不太會和外部表格頻繁關聯
  • 經營管理中心數據產品的接口層存儲,表格行數較大,單分區可以到4億行,不需要in,一般做靈活多維度彙總,併發較低
  • 商品評估中心數據產品的接口層存儲,表格行數單分區千萬行,一般不需要Join,不需要彙總,但需要根據條件在where中進行明細篩選

最後決定設立4個Table Group:

  • 公共層TG: 存放維表,明細表,公共彙總表,Local Join 以及彙總,分配較多資源
  • 營銷+大屏TG:大屏及營銷活動應用,數據量較少,主要用於歷史對比,實時應用併發讀寫較大;採用20個Shard, 之前300個Shard是極大的資源浪費
  • 經營管理中心TG:經營管理中心各明細粒度表格,設置50個Shard
  • 商品or行業TG:存放商品和類目結果數據,設置40個Shard
    6.png

2)次要問題1:表結構設計

數倉建設中,最重要的一個環節就是合理的設計表結構,包括表的數據類型、表的索引等。尤其是索引,合理的索引設計將會提高几倍甚至幾十倍的性能。通過重新梳理表結構,發現業務並沒有合理的設置表索引,這是導致性能不符合預期的原因之一,於是我們也對索引進行了改造。值得注意的是,當前和數據佈局有關的索引的建立必須要在建表初期完成,後面不可以更改/新增,獨立於數據佈局的索引,比如bitmap,可以後面再按需修改。所以需要提前根據場景設計好表結構,以免做重複工作。

distribution_key
  • 如果創建了Primary Key索引(也是唯一性約束,用於數據更新),默認為distribution_key。Distribution_key如果為空,默認是隨機分發。
  • 如果distribution_key設置不合理,數據會不均勻分佈於Shard中。計算過程中會產生Redistribute Motion算子數據重新分佈打散,帶來冗餘的網絡開銷。 如設置合理,則可以避免這種情況。
  • 通常設置關聯(Join)的列或Group by的列或分散更隨機的列作為distribution_key,來儘量打散數據到不同的Shard。請注意這裡選擇單列作為distribution_key即可。
Segment_key

分段鍵,用於文件塊的邊界劃分,查詢時基於Segment_key可以快速定位數據所在文件塊,選擇與寫入時間戳相關的字段在查詢時有加速的效果。一般用於時間戳這樣的時序數據,Segment_key通常只用一列,遵循左對齊原則。Segment_key使用的限制比較多,要求文件在向Hologres寫入時是按照Segment_key的順序排序完成後再寫入,即select後按照Segment_key進行order_by再寫入,才會生效;一般適用於純實時寫入的自增/類自增字段(e.g.下單時間)。

Clustering_key

聚簇索引,是文件內的排序列,用於範圍查詢(RangeQuery)的快速過濾。與MySQL的聚簇索引不同,Hologres用來佈局數據,不是佈局索引,因此修改Clustering_key,需要重新數據導入,且只有一組Clustering_key,一般Clustering_key不超過2列。通常建議將where條件裡面的篩選列設置為Clustering_key

Bitmap

位圖索引,對於等值過濾場景有明顯的優化效果,多個等值過濾條件,通過向量高效計算; 適用於啞變量(基數低)的列,相當於啞變量一列變多列的實現。

Dictionary_encoding

字典編碼列索引,可以將字符串的比較轉為數字的比較,對於字符串類型可以有效壓縮,特別是基數低的列,達到加速Group by,Filter的效果。Hologres在建表時會自動給text類型加上Bitmap索引和字典編碼列索引,以實現更優的性能,但是需要注意的是,在不滿足需要的場景下需要根據業務場景添加或刪除相應的索引,因為dictionary_encoding會消耗編碼解碼的資源。
下圖是Hologres索引匹配原則,可以通過該圖瞭解一下索引的執行原理:
7.png

  • 可以通過執行analyze參數,來獲取表的統計信息,幫助Hologres在讀取計算時將執行計劃優化。
analyze tablename;

下面結合具體的示例展示怎樣優化表結構:
8.png

table1是一個數千萬到億行的明細表,對其他表(維表)有頻繁Local Join的需求,和較大的併發寫入;
1)根據業務查詢和寫入需求,將表放在公共層的Table Group中並分配60個Shard來滿足讀寫需求。
2)因為是明細表,有大量的關聯和等值/篩選場景,添加了較為全面索引配置:

  • 分析場景應用較多,有大量聚合,Group by操作,選擇列存。
  • distribution_key:正常來講應該滿足常用Join的列+能儘量分散的列作為distribution_key;這張表作為明細表,很多列都會用於關聯,所以不太好選一個key出來,選多個key的話反而會造成性能下降(要全部key都被使用才有效),最後決定選擇較符合條件的id3作為distribution_key;
  • 這裡像id1和id2是啞變量字段,適合同時配置Bitmap索引和字典編碼列索引,方便Group by 和等值查詢;
  • 對於日期(ds)設置為分區字段。明細表在查詢和使用時日期都是必不可免的字段,通過設置分區,可以有效縮小每次查詢的掃描範圍;另一方面也可以較安全的進行運維和排查問題。
  • 當前表不適用Segment_key,因數據離線/實時兩種插入模式,排序成本較高,暫不設置。
  • 對於Clustering_key,按照使用頻次,目前的選擇是id1+id2。
  • 最終DDL如下(因涉及業務敏感數據,只展示部分DDL):
BEGIN;
CREATE TABLE public.table1 (
 "stat_date" text,
 "id0" text,
 .....,
 "id3" text,
 "id2" text,
 "name2" text,
 ...,
 "id1" text,
....,
 "ds" text NOT NULL
)
PARTITION BY LIST (ds);
--如果是用來新建TableGroup,則需要下面第一句,已有TableGroup則不需要
call set_table_property('table1', 'shard_count', '60');
CALL set_table_property('table1', 'distribution_key', 'id3');
CALL SET_TABLE_PROPERTY('public.table1', 'orientation', 'column');
CALL SET_TABLE_PROPERTY('public.table1', 'clustering_key', 'id1,id2');
CALL SET_TABLE_PROPERTY('public.table1', 'bitmap_columns', '...,id1,id2,...');
CALL SET_TABLE_PROPERTY('public.table1', 'dictionary_encoding_columns', '...,id1,id2,...');
CALL SET_TABLE_PROPERTY('public.table1', 'time_to_live_in_seconds', '7776000');
COMMIT;

3)次要問題2:應用緩存

對於重要高頻報表添加合適的緩存來緩解數據庫壓力,離線報表可以設置時間較長的緩存,實時報表可以考慮在應用端增加 5s, 10s, 30s,1min等多個檔位的緩存。

4)次要問題3:不合理壓測計劃

在之前幾次全鏈路壓測中,對於Hologres實例進行讀和查的多方面壓測,雖然壓測讀的量到位了,但是沒有同步壓測數據庫寫入峰值,在實際場景中讀的性能會受到寫入數據洪流的壓力和影響;尤其Hologres存在兩種主要的寫入方式(外表同步內表,實時寫入內表);在壓測和實際使用的過程中需要特別注意讀寫峰值一起壓測。

優化後業務效果

通過優化後,在雙11這天0點的流量高峰期,在0點寫入和Query讀取同時達到業務峰值的情況下,Hologres支持的數據產品的RT平均響應時間穩定在100ms左右,為使用數據產品的業務同學/分析同學,在雙11提供穩定毫秒級的實時OLAP決策數據支持。同時也非常平穩的支持了營銷活動中心&實時大屏等核心的高併發業務產品,以及BI同學實時取數分析等場景,CPU水位穩定在30%以下,內存水位也穩定在50%以下。
9.png

同時通過本次天貓雙11,我們也發現Hologres作為實時數據存儲,在分析方面有巨大的潛力,在滿足寫入性能的同時,一方面可以和現有離線數據關聯分析,另一方面是能支持高性能的OLAP分析數據。這也團隊後續使用Hologres作為數據團隊新實時數倉架構的核心組件奠定了基礎。

後續規劃

經過雙11之後,研發團隊下個階段將利用Hologres進行更大範圍的實時數倉改造:

  1. Hologres作為行存實時公共層(替代之前timetunnel作為新中間件)開放下游數據庫訂閱, 保持對內整個架構和對外多個架構的數據一致性,以及解決實時結果數據在timetunnel中不可見,二次操作成本高的痛點。
  2. 下游應用層訂閱公共層實時數據,應用層數據按照保障級別和local_Join的需要進行實例級別分割,資源隔離。
    舉例:以渠道數據化實例為例,這部分數據大部分對外開放給CRM系統和生態三方合作伙伴,對一致性,及時性和併發都有較高的要求,容易出現數據故障;在數據層面上也會有較頻繁的Local Join訴求;綜合來看作為單獨實例分割,包給予充足的資源保障。

10.png

Leave a Reply

Your email address will not be published. Required fields are marked *