全网首发|阿里资深技术专家数仓调优经验分享(上)
簡介:?本篇文章總結(jié)了AnalyticDB表的設(shè)計的最佳經(jīng)驗、數(shù)據(jù)寫入的最佳經(jīng)驗、高效查詢的最佳實踐,以及一些常見的問題。
隨著云原生數(shù)據(jù)倉庫AnalyticDB for MySQL(下文統(tǒng)一簡稱:AnalyticDB)在阿里集團各個業(yè)務線、社會上各行各業(yè)的推廣應用,我們沉淀了一些最佳實踐,現(xiàn)在筆者整理在這里,供大家參考,希望對大家有幫助。本篇文章總結(jié)了AnalyticDB表的設(shè)計的最佳經(jīng)驗、數(shù)據(jù)寫入的最佳經(jīng)驗、高效查詢的最佳實踐,以及一些常見的問題。
說明:
1.在讀這篇文章之前,請先了解AnalyticDB的產(chǎn)品官方文檔,以提前適當了解AnalyticDB;
2.本文寫的最佳實踐主要針對AnalyticDB 3.0,AnalyticDB 2.0在原理上也同樣適用。
01 表設(shè)計的最佳實踐
AnalyticDB,作為一個分布式數(shù)據(jù)倉庫,能夠為海量數(shù)據(jù)的實時分析帶來卓越的性能體驗。為了充分發(fā)揮AnalyticDB在數(shù)據(jù)分析方面的性能優(yōu)勢,設(shè)計表時,需要注意以下幾點規(guī)則。
(一)選擇合適的表類型(維度表or普通表)
· 維度表:又稱廣播表,是數(shù)據(jù)倉庫中的一個概念,一般存儲維度數(shù)據(jù)。在AnalyticDB中建表語句中有DISTRIBUTED BY BROADCAST的關(guān)鍵字,這些表會在集群的每個節(jié)點存儲一份數(shù)據(jù),因此維度表的數(shù)據(jù)量不宜太大,建議每張維度表存儲的數(shù)據(jù)不超過2萬行。
注意:維度表太大,會導致數(shù)據(jù)存儲空間的膨脹,節(jié)點越多膨脹越大,同時也會導致實時寫入時性能下降,IOPS會比較高。
· 普通表:也叫作分區(qū)表、事實表,一般存儲業(yè)務的主題數(shù)據(jù)。普通表可存儲的數(shù)據(jù)量通常比較大,可以存儲千萬條甚至萬億條數(shù)據(jù),可以通過一級分區(qū)對數(shù)據(jù)做分片以及二級分區(qū)對數(shù)據(jù)進行生命周期管理。
(二)選擇合適的分布鍵(一級分區(qū)鍵)
AnalyticDB中創(chuàng)建普通表時,默認需要通過DISTRIBUTED BY HASH(column_name,...)指定分布鍵,按照column_name的HASH值進行分區(qū)。
AnalyticDB支持將多個字段作為分布鍵。
分布鍵的選擇依據(jù):
- 盡可能選擇值分布均勻的字段作為分布鍵,例如交易ID、設(shè)備ID、用戶ID或者自增列作為分布鍵;
- 盡可能選擇參與JOIN的字段作為分布鍵,例如進行用戶畫像分析時,可以選擇user_id作為分布鍵。
注意:分布鍵不均勻容易導致數(shù)據(jù)分布不均,嚴重影響寫入和查詢的效率,此外也容易使單節(jié)點磁盤寫滿從而導致整個集群鎖定不可用。除特殊的業(yè)務場景外,建表優(yōu)先考慮數(shù)據(jù)是否均勻,然后再考慮JOIN KEY對齊的問題。
(三)選擇合適的分區(qū)鍵(二級分區(qū)鍵)
對于表的數(shù)據(jù)量非常大的表,需要考慮創(chuàng)建二級分區(qū)表來對數(shù)據(jù)做進一步的切分,設(shè)置了二級分區(qū)后,也能帶來兩個好處:
1)對數(shù)據(jù)進行生命周期管理,比如設(shè)置了一定數(shù)量的二級分區(qū)數(shù)量后,過期的二級分區(qū)會自動被淘汰掉;
2)當查詢條件帶上了二級分區(qū)字段時,是可以對二級分區(qū)進行裁剪的,從而提升查詢的性能。
- 直接用ds的值來做分區(qū) PARTITION BY VALUE(ds)
- ds轉(zhuǎn)換后的天做分區(qū) PARTITION BY VALUE(DATE_FORMAT(ds, '%Y%m%d'))
- ds轉(zhuǎn)換后的月做分區(qū) PARTITION BY VALUE(DATE_FORMAT(ds, '%Y%m'))
- ds轉(zhuǎn)換后的年做分區(qū) PARTITION BY VALUE(DATE_FORMAT(ds, '%Y'))
二級分區(qū)的注意事項:
請?zhí)崆耙?guī)劃好實例中所有表的二級分區(qū)鍵,充分利用二級分區(qū),不要讓每個二級分區(qū)的數(shù)據(jù)量過小,假如,用天進行二級分區(qū),每天數(shù)據(jù)量很小,那么可以考慮用月作為二級分區(qū)。二級分區(qū)數(shù)據(jù)量過小,會導致數(shù)據(jù)庫中需要保存分區(qū)數(shù)據(jù)的元數(shù)據(jù)特別多,而這些元數(shù)據(jù)存放在內(nèi)存中,過多的元數(shù)據(jù)會占據(jù)較多的內(nèi)存空間,導致系統(tǒng)的GC或者OOM,同時也會導致實時寫入的IOPS較高。
二級分區(qū)的數(shù)據(jù)量建議:
(四)選擇合適的主鍵
在表中定義主鍵可以實現(xiàn)數(shù)據(jù)消重(REPLACE INTO)和數(shù)據(jù)更新(DELETE、UPDATE)。只有定義過主鍵的表支持數(shù)據(jù)更新操作(DELETE、UPDATE)。
主鍵的選擇依據(jù):
- 盡可能選擇數(shù)值類型的單個字段作為主鍵,表的性能相對更好。
- 如果數(shù)值類型的單一主鍵無法滿足業(yè)務需要,也可以使用字符串或者多字段組合作為主鍵。
- 主鍵中必須包含分布鍵和分區(qū)鍵,如果表中定義了二級分區(qū)鍵的話,主鍵必須包含二級分區(qū)鍵。
注意:作為主鍵的字段不宜太大,字段的長度不宜過長,否則會影響寫入的性能。
(五)選擇合適聚集索引
聚集索引會將一個或者多個字段排序,保證該字段相同或者相近的數(shù)據(jù)存儲在磁盤的相同或相近位置,當以聚集索引中的字段作為查詢條件時,查詢結(jié)果保持在磁盤的相同位置,可以減少磁盤的IO。
聚集索引的選擇依據(jù):
查詢一定會攜帶的過濾條件的字段可以設(shè)計為聚集索引。例如,電商賣家透視平臺中每個賣家只訪問自己的數(shù)據(jù),賣家ID可以定義為聚集索引,保證數(shù)據(jù)的局部性,提升數(shù)據(jù)查詢性能。
注意:目前只支持一個聚集索引,但一個聚集索引可以包含多列。目前除非對非常分散的數(shù)據(jù)進行點查,否則聚集索引對性能的幫助很少。
(六)設(shè)計合適的數(shù)據(jù)類型
建議用戶盡可能使用數(shù)值類型,減少使用字符串類型。
AnalyticDB處理數(shù)值類型的性能遠好于處理字符串類型,原因在于:
- 數(shù)值類型定長,占用內(nèi)存少,存儲空間小。
- 數(shù)值類型計算更快,尤其是在數(shù)據(jù)關(guān)聯(lián)場景。
- 從內(nèi)部索引機制上,字符串類型適合等值查詢和范圍查詢,而時間類型、數(shù)值類型性能更好。
- 選擇盡可能小的字段長度,比如,性別可以使用Boolean或者Byte類型,數(shù)據(jù)長度不大的可以用Int類型。
- 在同一個業(yè)務模型內(nèi),相同字段設(shè)計成相同的數(shù)據(jù)類型和字段長度,字段命名也保持一致,特別是涉及到主外鍵關(guān)聯(lián)的字段更要注意,避免不同的數(shù)據(jù)類型的字段關(guān)聯(lián)導致隱式轉(zhuǎn)換。
常見字符串數(shù)據(jù)的處理建議:
- 包含字符前綴或后綴,例如E12345,E12346等。建議去掉前綴或者將前綴映射為數(shù)字。
- 字段只有少數(shù)幾個值,例如國家名。建議對每個國家編碼,每個國家對應一個唯一數(shù)字。
- 時間/日期類型數(shù)據(jù),避免使用Varchar字符類型存儲,盡量使用Date,Timestamp或者Int類型。
- 地理的經(jīng)度/緯度數(shù)據(jù),建議采用Double數(shù)據(jù)類型進行存儲。
如果您在建表前,不清楚自身業(yè)務的數(shù)據(jù)分布特征,可在數(shù)據(jù)導入后,使用優(yōu)化建議進行優(yōu)化。具體請訪問AnalyticDB控制臺的建表診斷頁面:數(shù)據(jù)建模優(yōu)化 - 云原生數(shù)倉 AnalyticDB MySQL - 阿里云,查看建表問題及優(yōu)化建議。
02 數(shù)據(jù)寫入的最佳實踐
(一)實時寫入
1.批量打包的方式提交
向表中寫入數(shù)據(jù)時,可以通過批量打包方式INSERT INTO和REPLACE INTO提高數(shù)據(jù)寫入性能。注意事項如下:
- 通過每條INSERT或者REPLACE語句寫入的數(shù)據(jù)行數(shù)需大于1000行,但寫入的總數(shù)據(jù)量不宜太大,不能超過16MB。
- 通過批量打包方式寫入數(shù)據(jù)時,單個批次的寫入延遲相對較高,但是整體性能有所提升。
- 寫入報錯時,需要重試以確保數(shù)據(jù)被成功寫入,重試導致的數(shù)據(jù)重復可以通過表的主鍵來消除。
- 如果不需要對原始的數(shù)據(jù)進行修改,可以使用INSERT INTO寫入數(shù)據(jù),效率是REPLACE INTO的3倍以上。
樣例:
INSERT INTO test ?(id, name,sex,age,login_time) ? VALUES ?(1,'dcs',0,23,'2018-03-02 10:00:00'), ?(2,'hl',0,23,'2018-03-02 10:01:00'), ?(3,'xx',0,23,'2018-03-02 10:02:00') ? ......;2.更新數(shù)據(jù)
數(shù)據(jù)更新有多種方式,使用區(qū)別如下:
- 高頻基于主鍵的行級覆蓋更新, 且應用可以補齊所有列,請使用REPLACE INTO VALUES批量打包。
- 高頻基于主鍵的行級覆蓋更新, 應用不能補齊所有列,請使用INSERT ON DUPLICATE KEY UPDATE批量打包。
- 低頻任意條件更新,請使用UPDATE SET WHERE。
注意:UPDATE需要查表來填補更新中缺失的舊值,因此比REPLACE INTO多一次查詢,性能較低,不建議做高頻、大批量的UPDATE操作。如果線上UPDATE性能無法滿足需求,需考慮替換成REPLACE INTO,由應用端填補舊值。
3.刪除數(shù)據(jù)
數(shù)據(jù)刪除有多種方式,使用區(qū)別如下:
- 低頻主鍵條件刪除,請使用 DELETE FROM WHERE primary key = xxx。
- 低頻任意條件刪除,請使用 DELETE FROM WHERE。
- 刪除單個二級分區(qū),請使用 TRUNCATE PARTITION。
- 刪除單表(包括所有二級分區(qū)),請使用TRUNCATE TABLE或DROP TABLE。
(二)批量導入
1.如何選擇批量導入還是實時導入
- 從ODPS、OSS導入AnalyticDB,推薦使用INSERT OVERWRITE SELECT做批量導入,有以下兩個原因:一,批量導入適合大數(shù)據(jù)量導入,性能好;二,批量導入適合數(shù)倉語義,即導入過程中舊數(shù)據(jù)可查,導入完成一鍵切換新數(shù)據(jù),如果導入失敗,新數(shù)據(jù)會回滾,不影響舊數(shù)據(jù)的查詢。
- 從RDS、MySQL、AnalyticDB等導入AnalyticDB,根據(jù)數(shù)據(jù)量情況,如果數(shù)據(jù)量不大(百萬級別的表),推薦使用INSERT INTO SELECT做實時導入;如果數(shù)據(jù)量較大,推薦使用INSERT OVERWRITE SELECT做批量導入。
- 對相同的一張表,不能既采用INSERT OVERWRITE SELECT又采用INSERT INTO SELECT操作,否則數(shù)據(jù)會被覆蓋。
2.導入并發(fā)和資源說明
- 單張表的導入會在系統(tǒng)內(nèi)部排隊串行,而多張表的導入,會產(chǎn)生n個并行導入任務(并行度可調(diào)整,默認并行度是2),出于資源控制的考慮,超出并行度的任務也會排隊。
- 數(shù)據(jù)導入,同查詢一樣,會消耗AnalyticDB實例的計算資源。因此,建議在查詢QPS較低時執(zhí)行數(shù)據(jù)導入,并推薦通過定時任務進行錯峰導入。
03 高效查詢的最佳實踐
AnalyticDB的優(yōu)勢是能在海量數(shù)據(jù)場景下,面對復雜查詢,做到實時的在線分析。AnalyticDB的查詢調(diào)優(yōu),不僅兼容數(shù)據(jù)庫查詢優(yōu)化的通用方法,還提供一些專門的優(yōu)化方法,使其能夠充分發(fā)揮出分布式計算的性能優(yōu)勢。
(一)查詢優(yōu)化的通用法則
按照葉正盛早些年在《ORACLE DBA手記》上寫的文章,數(shù)據(jù)訪問優(yōu)化滿足以下漏斗法則:
1.減少數(shù)據(jù)訪問(減少磁盤訪問)
盡量多的使用過濾條件,盡早的提前過濾數(shù)據(jù),從而減少參與計算的數(shù)據(jù)量,例如在子查詢里提前把能過濾的數(shù)據(jù)先過濾。
2.返回更少數(shù)據(jù)(減少網(wǎng)絡(luò)傳輸或磁盤訪問)
在OLAP數(shù)據(jù)庫中,由于表的列數(shù)往往比較多,且是基于列存或者行列混存,所以SELECT * 的操作,會導致較多的請求IO。因此,請盡量避免SELECT * 的查詢。
3.減少交互次數(shù)(減少網(wǎng)絡(luò)傳輸)
建議使用上文提到的批量導入,減少交互次數(shù)。
4.減少服務器CPU開銷(減少CPU及內(nèi)存開銷)
- 減少不必要的排序和分頁,特別是子查詢中的排序。
- 在滿足業(yè)務前提下,盡量減少COUNT DISTINCT操作。
- 在滿足業(yè)務前提下,特別是在海量數(shù)據(jù)下,采用類似Hyperloglog的近似計算代替準確計算。
5.利用更多資源(增加資源)
- 設(shè)計表的時候,盡量避免分區(qū)傾斜, 不要把存儲和計算壓在某一個節(jié)點上。建議盡量把數(shù)據(jù)都均勻的散列到所有的節(jié)點上,充分利用所有機器的能力,最大程度地發(fā)揮分布式數(shù)據(jù)庫的效能。
- AnalyticDB本身就是MPP大規(guī)模并行處理的典型系統(tǒng),在內(nèi)核層面做了大量的優(yōu)化處理,能夠充分利用更多的資源。
(二)AnalyticDB特殊場景的優(yōu)化
1.外表查詢的最佳實踐
- 不推薦使用外表進行復雜計算。外表計算會拉取全部數(shù)據(jù),因此外表的復雜計算會導致嚴重的GC,也會給網(wǎng)絡(luò)帶寬造成較大壓力。
- 外部表不支持DML操作(DELETE、UPDATE、TRUNCATED)。如果需要修改外表數(shù)據(jù),請到源表中進行DML操作。
2.合理的使用索引
合理使用索引是數(shù)據(jù)庫調(diào)優(yōu)的一個非常重要的手段,AnalyticDB也不例外。在AnalyticDB中,默認每列都會創(chuàng)建索引。但是也有例外情況。如果某列的Cardinality值較低,索引的選擇性不高,通過索引查詢,性能可能會更差。此時,建議在建表時關(guān)閉自動創(chuàng)建索引的功能。如果表已經(jīng)建好,可以使用如下SQL語句,刪除索引或者通過hint繞過索引。
ALTER TABLE table_name DROP INDEX index_name; --方法一:刪除枚舉列的索引 /+no_index_columns=[t_order_content.fdelete;fdbid]/ --方法二:通過hint使查詢繞過索引3.巧妙的使用聚集索引
當查詢條件一定包含某列,特別是該列數(shù)據(jù)在存儲上非常分散時,對該列建立聚集索引,性能會有明顯的提升。您可以采用類似如下的SQL語句添加聚集索引:
ALTER TABLE table_name ADD CLUSTERED INDEX index_cls (d_fdbid);注意:如果表中已經(jīng)有了數(shù)據(jù),直接ADD CLUSTER INDEX不會對存量的數(shù)據(jù)排序,需要重新建表,并在建表的時候加上聚集列關(guān)鍵字;或者在添加完聚集索引后對該表做一次build操作:build table table_name force=true。
4.減少節(jié)點間的數(shù)據(jù)交互
分布式數(shù)據(jù)庫,在充分發(fā)揮分布式計算優(yōu)勢的同時,有時也會加大跨節(jié)點間的網(wǎng)絡(luò)開銷。特別是請求的數(shù)據(jù)量較少,數(shù)據(jù)卻分散在較多節(jié)點的情況,跨網(wǎng)絡(luò)開銷的情況就非常明顯。本文提供以下兩個思路:
- 盡量在本地節(jié)點內(nèi)進行Join,充分利用Local Join特性,大大減少跨網(wǎng)絡(luò)訪問。具體做法為:盡量采用一級分區(qū)鍵關(guān)聯(lián);
- 盡量在本地節(jié)點內(nèi)進行聚合分析,減少跨網(wǎng)絡(luò)訪問shuffle的數(shù)據(jù)量。具體做法為:盡量對一級分區(qū)鍵進行GROUP BY。
04 AnalyticDB連接的最佳實踐
在使用方法上,AnalyticDB與MySQL的兼容程度高達99%以上,支持多種連接方式,包括MySQL命令行,JDBC連接,Python連接,C#連接,PHP連接等等。更詳細地使用方法,請參考官方文檔:連接集群 - 云原生數(shù)倉 AnalyticDB MySQL - 阿里云。
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。?
總結(jié)
以上是生活随笔為你收集整理的全网首发|阿里资深技术专家数仓调优经验分享(上)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 构建制品不一致,后续工作都是白费 | 研
- 下一篇: 贾扬清谈云原生-让数据湖加速迈入3.0时