Clickhouse寫入問題匯總
Zookeeper相關
當clickhouse在建表時使用了Replicated引擎族時, 會對zookeeper有非常重的依賴, 這時候就要注意zookeeper集群的一些優化項.
clickhouse集群兩分片兩副本(4C8G),實測每分鍾80W數據量的情況下, Zookeeper單機(4C8G)完全就能夠滿足要求了, 甚至還有很大的余地.
-
修改zookeeper的配置項
具體的參數說明可以看官網 Clickhouse Settings
clientPort=2181 dataDir=/data/zookeeper dataLogDir=/data/logs/zookeeper tickTime=2000 initLimit=30000 syncLimit=10 maxClientCnxns=2000 maxSessionTimeout=60000000 autopurge.snapRetainCount=10 autopurge.purgeInterval=1 preAllocSize=131072 snapCount=3000000 leaderServes=yes standaloneEnabled=false server.1=172.16.8.132:2888:3888
-
Zookeeper相關文件的存放
- zookeeper的snapshot文件存儲盤不低於1T, 注意清理策略.
- 將dataLogDir存放目錄應該與dataDir分開, 可單獨采用一套存儲設備來存放ZK日志, 最好用SSD.
-
多套Zookeeper集群配置一套Clickhouse集群.
1. Code 999
com.dtstack.jlogstash.outputs.core.common.ClickhouseException: ru.yandex.clickhouse.except.ClickHouseException: ClickHouse exception, code: 999, host: 172.16.8.84, port: 8123; Code: 999, e.displayText() = DB::Exception: Cannot allocate block number in ZooKeeper: Coordination::Exception: Connection loss (version 19.14.6.12)
ck與Zookeeper的連接丟失導致不能分配塊號等問題.
在Clickhouse中, 表的元數據信息, 每個數據塊的信息, 每次插入的時候, 數據同步的時候, 都需要和zookeeper進行交互. zookeerper 服務在同步日志過程中, 會導致ZK無法響應外部請求, 進而引發session過期等問題.
解決方法
-
參考上面zookeeper相關的優化.
-
在
zoo.cfg
中增加forceSync=no
默認是開啟的, 為避免同步延遲問題, zk接收到數據后會立刻去將當前狀態信息同步到磁盤日志文件中, 同步完成后才會應答. 將此項關閉后,客戶端連接可以得到快速響應.
關閉forceSync選項后, 會存在潛在風險, 雖然依舊會刷磁盤(
log.flush()
首先被執行), 但因為操作系統為提高寫磁盤效率, 會先寫緩存. 當機器異常后, 可能導致一些zk狀態信息沒有同步到磁盤, 從而帶來zkl前后信息不一樣問題. -
clickhouse建表的時候添加
use_minimalistic_part_header_in_zookeeper
參數, 對元數據進行壓縮存儲, 但是修改完了以后無法再回滾的.
Code 225
com.dtstack.jlogstash.outputs.core.common.ClickhouseException: ru.yandex.clickhouse.except.ClickHouseException: ClickHouse exception, code: 225, host: 172.16.8.84, port: 8123; Code: 225, e.displayText() = DB::Exception: ZooKeeper session has been expired. (version 19.14.6.12)
zk會話超時, 一般都是由於zk單機/集群出問題(例如zk服務掛了, zk的壓力太大)導致的.
解決方法
- 參考上面zookeeper相關的優化.
- zk client在與所有server斷開連接后(有可能是各種原因), client 會收到 disconnted消息. 當zk server 恢復后, zk client會自動與server連接上, 但是此時會話已失效, client收到 session expired消息. 前一個會話的所有數據均丟失. 接下來你要怎么做, 得看你的程序用途:
- 如果只是讀寫, 沒有主備機切換情況(就是當一台主機一台備機, 當主機掛機時, zk通知備機成為主機), 那么, 重新new一個會話, 將原來session的樹狀結構重新建立起來就行了.
- 如果是主備機切換情況, 那就不能簡單的建立 樹狀結構, 因為這時我們是不知道到底主機是掛掉了, 還是session expired了. 就只能把它當作真的是主機掛機來處理.
- ZK所有集群均不可用情況是比較少見的, 但是session expired需要引起重視起來.
- 一般情況下, 集群中一兩台機器的掛機和啟動, 我們都不用關心, apache的zk client可以幫我們自動處理這些問題.
Code 242
com.dtstack.jlogstash.outputs.core.common.ClickhouseException: ru.yandex.clickhouse.except.ClickHouseException: ClickHouse exception, code: 242, host: 172.16.8.84, port: 8123; Code: 242, e.displayText() = DB::Exception: Table is in readonly mode (version 19.14.6.12)
zookeeper壓力太大, 表處於“read only mode”模式, 導致插入失敗.
解決方法
其實和上面兩個問題Code 999/225
一樣, 都是由於zk集群的配置導致的, 所以只要想辦法增加zk集群的配置, 或者降低zk集群的壓力即可.
- 參考上面zookeeper相關的優化.
Code 1002
com.dtstack.jlogstash.outputs.core.common.ClickhouseException: ru.yandex.clickhouse.except.ClickHouseUnknownException: ClickHouse exception, code: 1002, host: 172.16.8.84, port: 8123; 172.16.8.84:8123 failed to respond
導致的原因是官方jdbc的實現用了httpclient的庫.
服務器的keep-alive時間已知為3s, 客戶端與服務端進行通信, httpclient會復用已創建的連接, 若服務端已關閉連接, 客戶端在沿用這個連接就會出現failed to respond
的錯誤.
解決方法
- 禁用HttpClient的連接復用.
- 重試方案: http請求使用重發機制, 捕獲
NohttpResponseException的
異常, 重新發送請求,重發N次后還是失敗才停止. - 根據keep Alive時間, 調整
validateAfterInactivity
小於keepAlive Time
, 但這種方法依舊不能避免同時關閉. - 系統主動檢查每個連接的空閑時間, 並提前自動關閉連接. 避免服務端主動斷開.
對於clickhouse的jdbc來說, 如果要提供方案, 就要在源碼層面更改:
-
方案1: 修改為短連接, 當然這種方式看你接不接受了, ClickHousePreparedStatementImpl中設置:
post.setHeader("Connection", "close");
-
方案2: 引入重試機制, 當出現這種錯誤的時候, 莫急莫慌, 再試一次.
這里我們采用的是方案2, 使用重試機制, 具體的重試次數可以通過參數bulkRetries
來控制.
推薦Insert操作使用方案2, Select操作使用方案1. 不需要考慮多次Insert同一數據導致數據重復的問題, CH內部有機制來保證數據唯一性.
Code 371
問題描述
An error occured before execution: Code: 371, e.displayText() = DB::Exception: Table 'test01' isn't replicated, but shard #1 is replicated according to its cluster definition (version 19.14.6.12)
只有使用了replicated開頭的engine的引擎的表, 才能夠在擁有on cluster xxx
條件的ddl語句中進行集群更新
其他engine的表, 只能夠每個node進行update.
distributed_table使用的是Distributed引擎, 所以也不支持on cluster xxx
這樣條件的ddl語句.
但是在最近的版本中(v20.8 LTS)中, Distributed引擎也支持了 on cluster 操作了, 感覺在低版本中不支持集群DDL語句可能是一個BUG.
所以建議條件允許的話, 將CH集群升級到20.8及以上版本即可.
解決方法
如果是分布式表
- 每個節點執行語句
如果是local表
- 每個節點執行語句
- 換用replicated的表引擎
Code 48
ClickHouse exception, code: 48, host: 172.16.8.84, port: 8123; Code: 48, e.displayText() = DB::Exception: There was an error on [172.16.8.84:9000]: Cannot execute replicated DDL query on leader (version 19.14.6.12)
這個問題我自己也沒有弄明白, 查了非常多的資料, 各執一詞. 在20.4及以后版本的一個pr修復了相關的問題:
Fix DDL worker timeouts for long queries
推薦升級到v20.8LTS版本.
如果不支持升級CH版本, 我嘗試在低版本上為每個分片加了一個副本也將這個問題解決了(原先的架構是無副本的).