最近更新时间:2025-12-15 17:57:02
这个问题在ClickHouse根因是ClickHouse merge的速度跟不上part的生成速度。建议从写入侧进行优化。
写入侧:
由于ClickHouse的单次insert会生成一个part,业务侧可以通过攒batch这种方式来降低clickhouse part的生成速度。
降低业务并发增大单次batch insert的值。
例如:
优化前:200并发、1w条数据、5min。
优化后:50并发、5W数据、10min。
首先zk的负载过高是由于引擎为replicated的表、insert、merge、replicated表数据复制请求zk频次太高导致,所以根本解决方法建议是减少此类操作。
insert的操作尽可能的通过调大batch的大小以此减少merge的操作。
如果zk的瓶颈在内存和磁盘可以适当的调整配置。
如果实例存在服务↔分布式表(replicated引擎),可以通过合并相关业务表适当的减少分布式表的数量,以此减少merge、insert与zk的请求频次。
因为zookeeper的写压力是没法通过增加节点数得到缓解,目前社区已经有相关的PR解决了,auxiliary zookeeper这个特性让可以在一个ClickHouse集群中配置多个zookeeper集群,以此分担单个zk集群负载压力问题。
如果业务侧仅需要将ClickHouse当作存储数据暂存、或者相关计算任务、高可用需求不高,可以采用多分片单副本架构采用写本地表(非replicated引擎)、读分布式表的策略,而云盘本身也保证了高可用。
ClickHouse目前为云盘架构读写带宽上限为250MB/s,首先确认是否带宽打满。
如果写入带宽未打满、存在部分读的流量,如果业务侧不存在相关读请求,那就是副本之间replicate表引擎的数据同步、分区的merge请求。
如果带宽未打满的情况下再确认zk性能达到瓶颈。
确保是不是查询导致hang住,可设置max_execution_time。
创建database
CREATE DATABASE wuhongda on CLUSTER default_cluster ;
default_cluster 为默认的cluster名称
创建本地表
CREATE TABLE wuhongda.table_name_local ON CLUSTER default_cluster (
v1 Int8,
v2 String,
v3 DateTime)
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{database}/{table}/{shard}', '{replica}')
PARTITION BY toYYYYMMDD(v3) ORDER BY v1 SETTINGS index_granularity = 8192
·PARTITION BY 会根据 v3 列的日期(转化为 YYYYMMDD 格式)将数据划分为不同的分区。toYYYYMMDD(v3) 将 v3 转换为以年、月、日(YYYYMMDD)为格式的整数。例如,2025-01-01 会被转换为 20250101。ClickHouse 会基于这个值将数据分配到不同的分区,这样有利于分区裁剪,提升查询性能。
·ORDER BY 用于定义数据在每个分区内的排序方式。在这个例子中,数据会按 v1 列的值进行排序,可以帮助提高按 v1 列过滤的查询效率。
创建分布式表
CREATE TABLE table_name_dis ON CLUSTER default_cluster (`v1` Int8,`v2` String,`v3` DateTime) ENGINE = Distributed(default_cluster,wuhongda,table_name_local, rand());
删除表
drop table table_name_local ON CLUSTER default_cluster;
drop table table_name_dis ON CLUSTER default_cluster;
确认是不是查询的分布式表。
确定分布式表底层本地表的存储引擎是否为replicated。
kill query where query_id= ''; #根据查询ID终止查询。
kill query where user = ''; #根据user终止查询。
业务报错信息:Timeout exceeded while receiving data from server. Waited for 300 seconds, timeout is 300 seconds. Cancelling query.
可能出现的原因:
锁等待导致超时。
底层数据节点磁盘打满导致不能进行操作(具体可联系金山云售后工程师进行确认)。
SELECT * FROM system.processes;类似于mysql中的show processlist
相关字段含义
字段 | 字段含义 |
user | 连接用户 |
address | IP地址信息 |
elapsed | 请求执行开始后的秒数 |
rows_read | 从表中读取的行数。对于分布式表来说,这是所有其它分片的总数 |
bytes_read | 从表中读取的未压缩字节数 |
total_rows_approx | 读取表数据的总行数近似值 |
memory_usage | 请求使用的内存占比 |
query | 查询文本。对于INSERT,它不包括要插入的数据 |
query_id | 查询ID |
is_cancelled | 查询是否被取消标识 |
is_all_data_sent | 是否将所有数据发送到客户端(换句话说,查询已经在服务器上完成) |
ClickHouse exception, code: 202, host: xxxxx, port: 8123; Code: 202, e.displayText() = DB::Exception: Too many simultaneous queries. Maximum: 100
原因:最大并发为100。
解决:
max_concurrent_queries 这个参数用来控制同时处理查询最大数量,建议客户观察实例负载情况,在控制台上对 max_concurrent_queries进行修改。
建议客户将多次insert语句合并为一次,同时检查system.processes,对当前执行的语句进行优化。
Received exception from server (version 21.1.2.15):Code: 48. DB::Exception: Received from XXXX:9000, ::1. DB::Exception: Mutations are not supported by storage Distributed.
原因:分布式表不能进行更新,ALTER TABLE UPDATE/DELETE不支持分布式DDL。
解决:需要在分布式环境中手动在每个节点上local的进行更新/删除数据。
read timeout
原因:查询超时导致报错。
解决:执行某些SQL很耗时导致最后报错读超时,这是因为ClickHouse执行单次SQL的默认最大等待时间是30s,如果有比较耗时的SQL, 可以通过将JdbcURL的socket_timeout参数值设置的大一点来解决这个问题(注意这个参数的时间单位是毫秒,默认是30000)。
DB::Exception: Received from localhost:9000. DB::Exception: Memory limit (for query) exceeded: would use 9.31 GiB (attempt to allocate chunk of 4223048 bytes), maximum: 9.31 GiB: While executing MergeTreeThread: While executing CreatingSetsTransform.
原因:内存使用超出限制,默认的最大限制是10G。
解决:sql设置单次查询内存或者设置用户配额(sql设置或者users.xml设置调整max_memory_usage = 20000000000000)
如果是group by内存不够,推荐配置上max_bytes_before_external_group_by参数,当使用内存到达该阈值,进行磁盘group by;
如果是order by内存不够,推荐配置上max_bytes_before_external_sort参数,当使用内存到达该阈值,进行磁盘order by;
如果是count distinct内存不够,推荐使用一些预估函数(如果业务场景允许),这样不仅可以减少内存的使用同时还会提示查询速度;
对于JOIN场景,需注意的是ClickHouse在进行JOIN的时候都是将"右表"进行多节点的传输的(右表广播),如果你已经遵循了该原则还是无法跑出来,那么只好扩内存或者分割数据跑出结果进行汇总。
Max query size exceeded
原因:单调sql的大小超过256kb。
解决:这是因为查询语句特别的大造成的,而默认的max_query_size最大是256 KB。打开/etc/clickhouse-server/users.xml(只配置了一些常用的用户)。max_query_size这类配置,需要在profiles部分中配置修改。
e.displayText() = DB::Exception: Too many partitions ,for single INSERT block (more than 100).
原因:单次插入的数据分区太多了,超过默认配置的 100 个了。
解决:
合理设置分区字段 。
修改这个 max_partitions_per_insert_block 参数,调大这个值。 偶发报错可以先设置session的值,set max_partitions_per_insert_block=xxx。
避免同一批次写入包含太多分区的数据。
DB::Exception: Too many parts (308). Merges are processing significantly slower than inserts.
原因:插入的速度太快了,ClickHouse合并的速度太慢。
解决:调小并行度,减少批次处理的条数。
小结:分区字段的设置要慎重考虑,如果每次插入涉及的分区太多,那么不仅容易出现上面的异常,同时在插入的时候也比较耗时,原因是每个数据目录都需要和zookeeper进行交互。
纯净模式
