化:分片策略與集群配置實(shí)戰(zhàn))
做ClickHouse運(yùn)維和開發(fā)這些年最常被問的問題不是“怎么安裝”而是“為什么我上了集群查詢反而變慢了”。明明節(jié)點(diǎn)一堆數(shù)據(jù)也放進(jìn)去了分布式查詢一跑就是幾秒甚至幾十秒排查到最后十有八九問題出在數(shù)據(jù)分片策略上。ClickHouse的分片設(shè)計(jì)直接影響分布式查詢能不能命中更少的節(jié)點(diǎn)、能不能減少網(wǎng)絡(luò)傳輸而這恰恰是很多人剛接觸集群時(shí)最容易忽略的部分。這篇文章就圍繞數(shù)據(jù)分片怎么定、分布式表怎么查、集群怎么配、坑怎么踩把ClickHouse分布式查詢性能這攤事講透。內(nèi)容適合正在規(guī)劃ClickHouse集群的架構(gòu)師、被線上慢查詢折磨的DBA也適合想系統(tǒng)理解分布式表原理的開發(fā)者。全文基于我實(shí)際運(yùn)維和壓測過的生產(chǎn)環(huán)境經(jīng)驗(yàn)給出的配置和排查思路都可以直接參考。1. 分片策略設(shè)計(jì)性能在寫入那一刻就決定了1.1 分片鍵選擇是分布式查詢性能的分水嶺很多人以為ClickHouse集群就是把數(shù)據(jù)均勻撒到每臺機(jī)器上查詢時(shí)全部節(jié)點(diǎn)一起算肯定比單機(jī)快。這個(gè)想法對了一半分布式查詢確實(shí)會(huì)并行下發(fā)到所有分片但“一起算”不代表“快”。查詢能不能裁剪到更少的分片取決于分片鍵和查詢條件的匹配程度。ClickHouse分片的原理很簡單寫入時(shí)按分片鍵的哈希值路由到某個(gè)分片查詢時(shí)分布式表會(huì)把SQL發(fā)到每個(gè)分片各分片算完一部分結(jié)果再匯總起來。如果分片鍵選得好查詢條件里帶了分片鍵那么查詢就可以只路由到少數(shù)幾個(gè)分片其他節(jié)點(diǎn)完全不參與速度自然快。如果分片鍵跟查詢條件對不上那就只能全量廣播到所有分片每個(gè)節(jié)點(diǎn)都掃一遍自己的數(shù)據(jù)中間結(jié)果還要通過網(wǎng)絡(luò)匯總性能必然暴跌。我見過一個(gè)典型案例一張用戶行為表按device_id做了哈希分片線上查詢卻幾乎都按app_version過濾結(jié)果每次查詢都廣播到全集群20個(gè)分片單次查詢掃描的數(shù)據(jù)量是實(shí)際需要的十幾倍。后來重建表的時(shí)候把分片鍵換成了app_version相關(guān)的字段雖然單個(gè)分片內(nèi)部的數(shù)據(jù)量不均衡了但查詢裁剪效果極其明顯P99延遲從3秒降到200毫秒。所以選擇分片鍵的第一原則盡量選擇高頻查詢條件里的等值過濾字段讓查詢能夠命中盡量少的分片。常見的可選方案有三種分片鍵方式數(shù)據(jù)分布查詢裁剪能力適用場景rand()最均勻冷熱均衡幾乎無法裁剪日志、事件流水等全量分析場景業(yè)務(wù)鍵哈希均勻可控按該鍵過濾時(shí)能裁剪用戶ID、訂單ID等強(qiáng)業(yè)務(wù)標(biāo)識低基數(shù)字段直接分片極不均勻按該字段過濾時(shí)命中精準(zhǔn)租戶、地域等隔離查詢場景有一類典型陷阱是用低基數(shù)字段直接做分片鍵比如按省份、按機(jī)房分片。表面上看每個(gè)分片對應(yīng)一個(gè)省份查詢單個(gè)省份時(shí)很爽但只要有一個(gè)省份的數(shù)據(jù)量是別的省份的十倍這個(gè)節(jié)點(diǎn)就會(huì)成為熱節(jié)點(diǎn)其他節(jié)點(diǎn)閑著它自己被拖垮。低基數(shù)字段更適合做WHERE里的過濾條件而不是分片鍵。如果業(yè)務(wù)查詢確實(shí)沒有固定維度比如純?nèi)罩痉治瞿怯胷and()也沒問題至少數(shù)據(jù)分布均勻。但這時(shí)候就要接受所有查詢都是全分片掃描集群的優(yōu)勢主要體現(xiàn)在CPU和磁盤并行上而不是智能裁剪上。1.2 分片數(shù)量與副本數(shù)量怎么定分片數(shù)量決定集群的擴(kuò)展粒度和查詢并行度。在ClickHouse里一個(gè)分片可以理解為一份數(shù)據(jù)的一個(gè)物理副本集合分片之間數(shù)據(jù)不重疊。分片數(shù)量通常由數(shù)據(jù)總量和單機(jī)處理能力決定。經(jīng)驗(yàn)值上單個(gè)副本的服務(wù)器建議配置不低于16核CPU、64GB內(nèi)存數(shù)據(jù)量在單機(jī)1TB到5TB之間比較舒服。如果總數(shù)據(jù)量是20TB那8個(gè)分片左右是底線加上磁盤冗余和查詢并發(fā)10到12個(gè)分片更穩(wěn)妥。分片太少會(huì)導(dǎo)致單節(jié)點(diǎn)壓力大分片太多則協(xié)調(diào)節(jié)點(diǎn)合并結(jié)果的開銷變大小查詢也可能被放大成大規(guī)模并行任務(wù)。副本數(shù)量的選擇也很關(guān)鍵。ClickHouse的副本默認(rèn)依賴ZooKeeper或ClickHouse Keeper進(jìn)行元數(shù)據(jù)同步副本不是越多越好。生產(chǎn)環(huán)境我一般建議一個(gè)分片至少2個(gè)副本保證節(jié)點(diǎn)宕機(jī)時(shí)查詢不受影響。副本數(shù)量超過3個(gè)后收益會(huì)明顯遞減反而增加了ZooKeeper的通信壓力。如果一個(gè)分片的副本間數(shù)據(jù)完全一樣寫入時(shí)需要注意internal_replication這個(gè)配置它設(shè)置為true時(shí)分布式表只向該分片的一個(gè)副本寫入由集群內(nèi)部同步到其他副本設(shè)置為false時(shí)分布式表會(huì)向該分片的所有副本各自寫入適合底層存儲不支持自動(dòng)復(fù)制的場景。從性能角度說查詢時(shí)分布式表會(huì)優(yōu)先選擇一個(gè)本地副本prefer_localhost_replica默認(rèn)開啟這樣當(dāng)前節(jié)點(diǎn)如果正好持有數(shù)據(jù)就不用走網(wǎng)絡(luò)。對多副本集群這個(gè)默認(rèn)行為能省不少內(nèi)網(wǎng)帶寬。1.3 分區(qū)和分片不是一回事別再搞混這是新手最容易混淆的兩個(gè)概念。分片解決的是“數(shù)據(jù)存在哪臺機(jī)器上”的問題分區(qū)解決的是“一塊數(shù)據(jù)在單臺機(jī)器內(nèi)怎么組織”的問題。分片是集群維度分區(qū)是表內(nèi)維度。比如說一張訂單表按order_id哈希分片到3臺機(jī)器每臺機(jī)器上的本地表又按toYYYYMM(create_time)做了月度分區(qū)。查詢的時(shí)候分布式表先通過分片鍵裁剪到命中節(jié)點(diǎn)節(jié)點(diǎn)內(nèi)部再通過分區(qū)裁剪跳過無關(guān)月份的數(shù)據(jù)。兩套機(jī)制是疊加生效的缺一不可。很多慢查詢優(yōu)化的第一刀不是加索引而是核對分片和分區(qū)是否都做到了裁剪。著名的經(jīng)驗(yàn)法則是如果查詢掃描的分區(qū)數(shù)量從12個(gè)降到1個(gè)往往比加索引更有效。分區(qū)字段一般選擇時(shí)間天、月、年配合TTL還能自動(dòng)清理過期數(shù)據(jù)這屬于另一塊大話題但和分片策略是配合使用的。2. 分布式表查詢機(jī)制與性能要點(diǎn)2.1 Distributed表引擎的寫入與查詢路徑分布式表引擎為Distributed本身不存儲任何數(shù)據(jù)它只是一個(gè)路由層。寫入時(shí)分布式表根據(jù)分片鍵計(jì)算出目標(biāo)分片把數(shù)據(jù)異步轉(zhuǎn)發(fā)給對應(yīng)節(jié)點(diǎn)上的本地表。查詢時(shí)分布式表把SQL改寫后下發(fā)到所有需要參與的分片各分片的本地表執(zhí)行完局部查詢再把結(jié)果返回給發(fā)起節(jié)點(diǎn)由發(fā)起節(jié)點(diǎn)做最終聚合、排序、LIMIT等操作。這套機(jī)制決定了分布式查詢的性能瓶頸通常不在計(jì)算而在兩個(gè)地方一是網(wǎng)絡(luò)傳輸量二是發(fā)起節(jié)點(diǎn)的合并開銷。比如一個(gè)大查詢每個(gè)分片返回幾百萬行中間結(jié)果匯聚節(jié)點(diǎn)要先把這些結(jié)果全部收進(jìn)來再排序內(nèi)存壓力極大。理解這個(gè)路徑之后很多優(yōu)化手段就順理成章了。首先盡量讓W(xué)HERE條件能夠下推到分片內(nèi)部執(zhí)行不要等所有數(shù)據(jù)匯到協(xié)調(diào)節(jié)點(diǎn)再做過濾其次LIMIT條件也會(huì)下推分片內(nèi)先各自截?cái)嗫梢源蠓鶞p少傳輸量再次ORDER BY同樣會(huì)下推各分片局部排序后再合并協(xié)調(diào)節(jié)點(diǎn)的壓力會(huì)小很多。還有一點(diǎn)容易被忽略分布式表查詢每個(gè)分片使用的連接數(shù)默認(rèn)是max_threads如果查詢并發(fā)高每個(gè)節(jié)點(diǎn)打開的連接數(shù)會(huì)非常多。適當(dāng)控制查詢并發(fā)和分片數(shù)比盲目擴(kuò)容分片更實(shí)際。2.2 GLOBAL JOIN為什么能救命又能害人分布式表上做JOIN是ClickHouse的經(jīng)典痛點(diǎn)。普通JOIN在分布式表上的行為是每個(gè)分片執(zhí)行查詢時(shí)發(fā)現(xiàn)自己需要關(guān)聯(lián)的數(shù)據(jù)在別的分片就會(huì)去遠(yuǎn)端拉取結(jié)果產(chǎn)生近似于NMFN倍網(wǎng)絡(luò)放大的效果。分片越多放大越嚴(yán)重很多分布式慢查詢就是這么來的。GLOBAL JOIN的原理是先把右表的數(shù)據(jù)全部拉到協(xié)調(diào)節(jié)點(diǎn)做成一個(gè)全局哈希表再把這個(gè)哈希表分發(fā)到每個(gè)參與查詢的分片。這樣每個(gè)分片只需要一次本地哈希查找網(wǎng)絡(luò)傳輸次數(shù)被限制住了。GLOBAL JOIN不是銀彈。它要求右表數(shù)據(jù)量足夠小能放進(jìn)內(nèi)存哈希表。如果右表是幾億行的分布式大表做GLOBAL JOIN會(huì)把協(xié)調(diào)節(jié)點(diǎn)直接打爆。更合理的做法是先把右表在本地節(jié)點(diǎn)物化成一張聚合后的緊湊表再用于關(guān)聯(lián)?;蛘呤褂肎LOBAL IN加子查詢配合子查詢內(nèi)的聚合減少數(shù)據(jù)傳輸量。實(shí)際經(jīng)驗(yàn)在千萬級用戶維表關(guān)聯(lián)場景下GLOBAL JOIN配合右表按分片鍵預(yù)先分布能獲得接近單機(jī)的性能但如果右表數(shù)據(jù)量過億就必須考慮改用字典表或者預(yù)聚合純SQL層面的JOIN優(yōu)化空間已經(jīng)不大了。2.3 分布式查詢的幾個(gè)關(guān)鍵參數(shù)參數(shù)作用我的建議prefer_localhost_replica查詢優(yōu)先命中本機(jī)副本默認(rèn)開啟不要關(guān)optimize_skip_unused_shards對分片鍵做等值過濾時(shí)跳過無關(guān)分片建議開啟前提是分片鍵與查詢條件匹配load_balancing多副本時(shí)選擇查詢副本的策略random或nearest_hostname均可max_threads單查詢并發(fā)線程數(shù)根據(jù)CPU核數(shù)調(diào)整不要盲目拉高max_memory_usage單查詢最大內(nèi)存分布式聚合場景務(wù)必設(shè)置合理上限optimize_skip_unused_shards這個(gè)參數(shù)很多人不知道。它開啟后如果查詢的WHERE條件里包含了分片鍵的等值表達(dá)式協(xié)調(diào)節(jié)點(diǎn)可以在下發(fā)查詢前就判斷出只需要訪問哪幾個(gè)分片其他分片完全不喚醒。對分片數(shù)量多的集群這個(gè)優(yōu)化效果非常明顯。還有個(gè)小技巧如果集群里所有節(jié)點(diǎn)查詢都會(huì)命中本地副本可以開啟allow_experimental_parallel_reading_from_replicas新版中已經(jīng)改名或成為默認(rèn)讓一個(gè)查詢同時(shí)并發(fā)讀取同一個(gè)分片的多個(gè)副本每個(gè)副本只讀一部分?jǐn)?shù)據(jù)這個(gè)特性在單分片性能吃緊時(shí)很管用。3. 從零搭一個(gè)三節(jié)點(diǎn)ClickHouse集群并驗(yàn)證分片效果3.1 配置文件與網(wǎng)絡(luò)規(guī)劃紙上談兵說再多不如跑一個(gè)最小集群。我用三個(gè)節(jié)點(diǎn)ch01、ch02、ch03搭建一個(gè)3分片集群每節(jié)點(diǎn)只部署一個(gè)分片的一個(gè)副本總分片3總副本3。這套方案足夠驗(yàn)證分片策略對查詢性能的影響也方便后續(xù)擴(kuò)展。ClickHouse的集群定義在config.xml的remote_servers段生產(chǎn)環(huán)境一般單獨(dú)抽成metrika.xml引入。關(guān)鍵配置如下clickhouse remote_servers clickhouse_cluster shard replica hostch01/host port9000/port /replica /shard shard replica hostch02/host port9000/port /replica /shard shard replica hostch03/host port9000/port /replica /shard /clickhouse_cluster /remote_servers macros shard01/shard replicach01/replica /macros /clickhousemacros段非常重要它讓分布式DDL可以在所有節(jié)點(diǎn)上自動(dòng)替換成對應(yīng)的本地宏變量建表、刪表就不需要逐節(jié)點(diǎn)手工執(zhí)行了。三個(gè)節(jié)點(diǎn)的macros分別寫成01/ch01、02/ch02、03/ch03。集群建好后用一條SQL確認(rèn)連通性SELECT hostName() AS node, count() FROM cluster(clickhouse_cluster, system.one) GROUP BY node;能返回三條記錄說明集群網(wǎng)絡(luò)層已經(jīng)打通。3.2 建表與分片效果驗(yàn)證建一張分布式表和對應(yīng)的本地表。本地表負(fù)責(zé)實(shí)際存儲分布式表用來路由。兩條DDL都通過ON CLUSTER下發(fā)保證集群內(nèi)所有節(jié)點(diǎn)執(zhí)行一致。-- 本地表 CREATE TABLE user_events_local ON CLUSTER clickhouse_cluster ( user_id UInt64, event_time DateTime, event_type String, amount Decimal(18,2) ) ENGINE MergeTree() PARTITION BY toYYYYMM(event_time) ORDER BY (user_id, event_time); -- 分布式表 CREATE TABLE user_events_dist ON CLUSTER clickhouse_cluster AS user_events_local ENGINE Distributed( clickhouse_cluster, default, user_events_local, cityHash64(user_id) );寫入測試數(shù)據(jù)時(shí)分布式表會(huì)自動(dòng)根據(jù)cityHash64(user_id)分流到對應(yīng)分片。如果想驗(yàn)證數(shù)據(jù)分布是否均勻可以用下面的SQL查看每個(gè)節(jié)點(diǎn)各自處理的行數(shù)SELECT hostName() AS node, count() AS rows FROM user_events_dist GROUP BY node ORDER BY node;由于user_id本身是一個(gè)分布均勻的字段三個(gè)節(jié)點(diǎn)的行數(shù)應(yīng)該非常接近。再看另一種情況如果把分片鍵換成event_type這種基數(shù)很低的字段三者行數(shù)就可能出現(xiàn)嚴(yán)重傾斜。實(shí)際測試時(shí)我曾經(jīng)往一張按月份字符串分片的表里灌數(shù)據(jù)結(jié)果70%的數(shù)據(jù)全落在同一個(gè)節(jié)點(diǎn)上分布式集群活生生用成了單機(jī)。建表之后建議順手驗(yàn)證一下optimize_skip_unused_shards的效果把三層配置文件里optimize_skip_unused_shards1/optimize_skip_unused_shards打開然后分別執(zhí)行不帶分片鍵過濾和帶分片鍵過濾的同一條聚合查詢觀察system.query_log里的read_rows和read_bytes。帶分片鍵過濾的查詢掃描行數(shù)應(yīng)該會(huì)明顯下降這驗(yàn)證了分片裁剪在實(shí)際查詢中確實(shí)生效。3.3 性能驗(yàn)證思路單節(jié)點(diǎn) vs 分布式我在驗(yàn)證分片效果時(shí)通常會(huì)在同一份數(shù)據(jù)上做三組對比本地表查詢、分布式表全掃描、分布式表按分片鍵裁剪。對比指標(biāo)只看兩個(gè)掃描行數(shù)read_rows和總耗時(shí)。一組典型的對比結(jié)果是這樣的10億行數(shù)據(jù)3節(jié)點(diǎn)集群查詢方式掃描行數(shù)耗時(shí)單節(jié)點(diǎn)本地表全表聚合10億8.2s分布式表全分片聚合10億2.9s分布式表按分片鍵過濾聚合約333萬0.4s分布式表全分片聚合只比單節(jié)點(diǎn)快約三倍這符合三節(jié)點(diǎn)并行的預(yù)期。按分片鍵過濾后由于裁剪掉兩個(gè)分片掃描量下降兩個(gè)數(shù)量級耗時(shí)自然大幅縮短。這說明分片鍵和查詢條件的匹配程度對性能影響遠(yuǎn)比“集群多不多”更大。如果手里沒有現(xiàn)成的ClickHouse環(huán)境可以用以下方式模擬在本地寫一個(gè)簡單的Python腳本插入100萬行user_id均勻的測試數(shù)據(jù)然后用三種不同的分片鍵分別建表對比。實(shí)踐下來這個(gè)過程中的體驗(yàn)比看任何文檔都深刻。4. 典型故障排查與部署經(jīng)驗(yàn)4.1 重啟報(bào)錯(cuò)“failed to flush system log already exists”怎么辦這個(gè)話題在社區(qū)里搜的人非常多幾乎每個(gè)經(jīng)歷過ClickHouse異常重啟的人都見過類似日志。報(bào)錯(cuò)原因是ClickHouse重啟時(shí)需要把系統(tǒng)日志表system.query_log、system.query_thread_log等flush到磁盤但元數(shù)據(jù)發(fā)現(xiàn)某些日志表已經(jīng)存在發(fā)生沖突導(dǎo)致啟動(dòng)流程中斷。常見誘因有三個(gè)異常kill導(dǎo)致日志表數(shù)據(jù)文件不完整、多副本節(jié)點(diǎn)同時(shí)操作了系統(tǒng)表、或者手動(dòng)改過system庫的表結(jié)構(gòu)。處理思路分為兩步。第一步嘗試通過SQL清理日志表前提是實(shí)例能起來SYSTEM FLUSH LOGS; TRUNCATE TABLE system.query_log; TRUNCATE TABLE system.query_thread_log; TRUNCATE TABLE system.trace_log;如果實(shí)例已經(jīng)起不來可以先用臨時(shí)配置禁用系統(tǒng)日志表把query_log、trace_log、query_thread_log等配置項(xiàng)臨時(shí)置為removetrue/remove或用空配置覆蓋讓實(shí)例跳過系統(tǒng)表的初始化直接啟動(dòng)然后清理舊日志表再恢復(fù)原配置重啟。更穩(wěn)妥的做法是在config.xml里對系統(tǒng)日志表加上engineEngine MergeTree/engine或使用Buffer時(shí)注意刷盤策略減少異常重啟時(shí)日志表文件損壞的概率。關(guān)鍵教訓(xùn)是不要隨意DROPsystem庫下的表這些表和系統(tǒng)表引擎強(qiáng)關(guān)聯(lián)處理不當(dāng)會(huì)導(dǎo)致更多元數(shù)據(jù)不一致問題。還有一個(gè)容易被忽略的點(diǎn)如果集群里多副本共用同一個(gè)system.path也就是數(shù)據(jù)目錄被多個(gè)實(shí)例共享也會(huì)觸發(fā)類似報(bào)錯(cuò)。確認(rèn)每個(gè)節(jié)點(diǎn)數(shù)據(jù)目錄獨(dú)立是避免這類問題的基礎(chǔ)前提。4.2 Windows、銀河麒麟等環(huán)境下的部署體驗(yàn)說到部署很多人會(huì)在Windows上想當(dāng)然地跑ClickHouse服務(wù)端。官方長期并不提供Windows原生服務(wù)端支持Windows下常見的做法是WSL2或Docker。WSL2跑ClickHouse做學(xué)習(xí)驗(yàn)證是沒問題的但性能損失明顯而且Windows文件系統(tǒng)和Linux文件系統(tǒng)之間的IO語義差異可能導(dǎo)致意外損壞生產(chǎn)環(huán)境千萬不要這么干。銀河麒麟這類國產(chǎn)系統(tǒng)上部署ClickHouse思路和CentOS/RHEL類似核心是離線安裝包。ClickHouse官方提供tgz離線包解壓后配置好環(huán)境變量和systemd服務(wù)即可。有幾個(gè)細(xì)節(jié)需要注意下載包時(shí)選擇與操作系統(tǒng)架構(gòu)匹配的版本x86和ARM的二進(jìn)制不能混用離線安裝時(shí)提前準(zhǔn)備好libicu等動(dòng)態(tài)庫依賴另外單機(jī)二進(jìn)制方式啟動(dòng)時(shí)clickhouse-server會(huì)自動(dòng)探測CPU指令集個(gè)別老CPU上啟動(dòng)會(huì)報(bào)非法指令解決辦法是換用兼容版本或在編譯參數(shù)上調(diào)整。銀河麒麟上部署我還踩過一個(gè)包管理器源沖突的坑卸載舊版本時(shí)殘留了/etc/clickhouse-server下的配置目錄導(dǎo)致新版本起不來。卸載后務(wù)必確認(rèn)配置目錄和/var/lib/clickhouse數(shù)據(jù)目錄完全清理干凈再重裝。4.3 數(shù)據(jù)傾斜的發(fā)現(xiàn)與緩解分片數(shù)據(jù)傾斜是分布式集群的頭號性能殺手。傾斜的發(fā)現(xiàn)辦法很簡單用分布式表按節(jié)點(diǎn)分組統(tǒng)計(jì)行數(shù)、字節(jié)數(shù)、分區(qū)數(shù)對比差異。也可以在system.parts表上按hostName()和partition_id聚合看得更細(xì)。SELECT hostName() AS node, sum(bytes_on_disk) AS total_bytes FROM cluster(clickhouse_cluster, system.parts) WHERE active 1 GROUP BY node ORDER BY total_bytes DESC;如果某個(gè)節(jié)點(diǎn)的磁盤用量是其他節(jié)點(diǎn)的兩倍以上基本可以斷定分片鍵選擇存在傾斜問題。緩解傾斜需要區(qū)分是“鍵本身傾斜”還是“數(shù)據(jù)隨時(shí)間自然傾斜”。鍵本身傾斜比如按租戶ID分片但租戶體量差異巨大最有效的手段是引入復(fù)合分片鍵。生產(chǎn)上我常用的一種方案是cityHash64(concat(tenant_id, _, cityHash64(user_id))) % shards_num這樣既保留租戶維度的部分親和性又避免單個(gè)租戶壓垮單個(gè)節(jié)點(diǎn)。如果業(yè)務(wù)上做不到可以在應(yīng)用層按租戶大小打散后再寫入。數(shù)據(jù)隨時(shí)間自然傾斜例如歷史數(shù)據(jù)集中在一段時(shí)間段跨節(jié)點(diǎn)后某些分片存儲被塞滿這種情況更適合用ALTER TABLE ... MOVE PARTITION把部分分區(qū)從熱節(jié)點(diǎn)遷移到冷節(jié)點(diǎn)。ClickHouse支持跨分片移動(dòng)分區(qū)到任意節(jié)點(diǎn)的本地表實(shí)現(xiàn)手工再平衡。不過要提醒一句傾斜一旦寫入就很難自動(dòng)恢復(fù)重建表是成本最低的糾正手段。在數(shù)據(jù)量可控的時(shí)候重新建表、分片鍵優(yōu)化、重新灌數(shù)往往比重做搬遷邏輯更省心。最后分享兩個(gè)實(shí)操體會(huì)第一個(gè)體會(huì)是分片鍵和查詢條件的匹配比集群規(guī)模重要得多。一個(gè)分片鍵設(shè)計(jì)合理的3節(jié)點(diǎn)集群性能往往好過一個(gè)分片鍵糟糕的10節(jié)點(diǎn)集群。規(guī)劃ClickHouse集群時(shí)先想清楚線上查詢會(huì)按什么字段過濾再?zèng)Q定怎么分片順序不要反。第二個(gè)體會(huì)是分布式查詢性能瓶頸多數(shù)在網(wǎng)絡(luò)傳輸和匯總開銷而不是單節(jié)點(diǎn)計(jì)算能力。所以做性能測試時(shí)不要只盯單條SQL的執(zhí)行計(jì)劃還要觀察system.query_log里的read_bytes、memory_usage、result_rows這三個(gè)字段它們能直觀反映查詢是否走了最優(yōu)路徑。ClickHouse這種工具配置項(xiàng)再多也不可怕關(guān)鍵是找到真正影響性命的那幾個(gè)開關(guān)然后持續(xù)用數(shù)據(jù)說話。