clickhouse数据库parts无法合并问题

现象:升级clickhouse版本后,出现如下报错,该报错一般出现于数据高速写入时,merge的速度赶不上part产生的速度,导致part数量堆积过多。

DB::Exception: Too many parts (300). Parts cleaning are processing significantly slower than inserts (version 21.4.6.55 (official build))

在clickhouse中,MergeTree家族的engine为了最大化保证写入速度,每个线程的每次插入,都会产生一个part,同一个partition内的多个part则会在后台进行merge,合并成一个更大的part。

clickhouse除了会控制单表层面的part数量之外,为了减轻merge压力(merge一般是在同一个partition内进行),它同样还会控制单个partition内的part数目。道理和前面一样,单个parttition内的part数目过多,自然要经过很多轮merge才能形成最终态,这个过程是非常吃CPU的,势必会影响到写入和查询。

clickhouse使用parts_to_throw_insert来控制单个partition内的part数量上限,如果单个partition内的part数量超过了这个上限,同样会报too many parts(300) 的错误。这个上限是多少呢?默认值是300。

--- 查看系统默认配置
SELECT
    name,
    value
FROM system.merge_tree_settings
WHERE name IN ('max_parts_in_total', 'parts_to_throw_insert', 'inactive_parts_to_throw_insert')

Query id: 88eba13a-5fe8-476d-bb7b-5b6353d9142b

┌─name───────────────────────────┬─value──┐
│ parts_to_throw_insert          │ 300   │
│ inactive_parts_to_throw_insert │ 0      │
│ max_parts_in_total             │ 100000 │
└────────────────────────────────┴────────┘

--- 查单表的总part数目
SELECT count()
FROM system.parts
WHERE (database = '<database>') AND (table = '<table>') AND (active = '1')

Query id: c3dec6f3-da47-441e-beed-682536390c46

┌─count()─┐
│  131150 │
└─────────┘

--- 查单个partition内最多的part数目
SELECT
    name,
    count() AS cnt
FROM system.parts
WHERE (database = '<database>') AND (table = '<table>') AND (active = '1')
GROUP BY name
ORDER BY cnt ASC
LIMIT 1

查看后台Merge线程池配置和当前线程使用情况,每个clickhouse节点上都是独立的线程池,互不干扰

SELECT
    metric,
    value
FROM system.metrics
WHERE metric LIKE 'BackgroundMergesAndMutationsPool%'

Query id: ffb22e4e-6114-4711-a023-385795dc4315

┌─metric───────────────────────────────┬─value─┐
│ BackgroundMergesAndMutationsPoolTask │     0 │
│ BackgroundMergesAndMutationsPoolSize │    64 │
└──────────────────────────────────────┴───────┘

2 rows in set. Elapsed: 0.002 sec.

下面是两个参数的官方解释说明

BackgroundMergesAndMutationsPoolSize
Limit on number of active merges and mutations in an associated background pool
BackgroundMergesAndMutationsPoolTask
Number of active merges and mutations in an associated background pool

经过上述分析之后决定将parts_to_throw_insert由300改为3000,修改之后出现更严重的问题,导致zookeeper频繁掉线选举,clickhouse无法写入

此时注意到一个异常值,zookeeper中的znode节点数量3100万,这个zookeeper集群只提供给clickhouse使用,所以znode全部属于clickhouse,zookeeper中的数据存储结构是多叉树的结构,下面对数据目录进行说明

主要分为下面图片中的四层:在这里插入图片描述

***先说第一层***
[zk: localhost:2181(CONNECTED) 3] ls /clickhouse/tables/cncc_dev/cncc/metric_series/1
[alter_partition_version,metadata,temp,log,leader_election,columns,blocks,nonincrement_block_numbers,replicas,quorum,pinned_part_uuids,block_numbers,mutations,part_moves_shard]

	/clickhouse:是clickhouse启动后在zookeeper中创建的持久化节点
	/tables:是clickhouse启动后在zookeeper中创建的持久化节点
	/cncc_dev: clickhouse集群名称
	/cncc: clickhouse数据库名称
	/metric_series: 表名
	/1: shard(shard为clickhouse中的分片,可简单理解为一个clickhouse节点)
	
	alter_partition_version: 用于跟踪特定分区的 ALTER 操作版本。当某个节点执行了 ALTER 操作后,它会在 ZooKeeper 的这个节点下记录相关的版本信息。其他副本节点会定期检查这个节点,以了解是否有新的 ALTER 操作需要执行。通过这种方式,ClickHouse 可以确保 ALTER 操作在所有副本上都被正确地同步和执行。
	metadata: 存储可复制表的一部分元数据信息,例如主键 分区键 之类的信息
	temp: 临时目录,存储一些临时信息
	log: 目录作为任务队列,对于存储可复制表的操作任务
	leader_election: 用于副本之间选主。查询时会优先选择主副本,另一个副本根据根据这个副本同步。多个副本可以在同一时间都成为leader。
	columns: 存储可复制表之中的字段信息
	blocks: 存储一段时间内写入此表的数据块的hash信息,用以去重。下面的子节点的格式为 分区名_hash值_hash值。
	nonincrement_block_numbers: **没找到说明**
	replicas: 存储副本信息
	quorum: 与是否配置 insert_quorum 有关
	block_numbers:存储表的所有的分区
	mutations:作为可复制表的 mutation 操作的 任务队列,将mutations的操作任务存储在此节点下

***第二层***
	这一层下面存储都是IP地址,代表当前表的当前shard存储在那个节点上

***第三层***
	is_lost: 标记副本是否过时,依据log_pointer是否是最新的,0为正常,-1为过时,1为修复中
	metadata: 存储表的元数据信息,同上述的metadata
	is_active: 是否存活,如果服务器异常,会不存在这个节点,恢复后会重新添加进来
	mutation_pointer: 存储下一个应该拉取的 mutations 队列之中的任务
	columns: 存储表的列信息
    log_pointer: 存储下一个应该拉取的log队列之中的任务
    parts: 存储表之中的所有parts,每个part中包含checksums和columns信息
    queue: 临时处理队列,主要用于副本之间保持数据同步

***第四层***
	parts: 下面的内容是每次insert插入产生的part文件,clickhouse后台会对当前分区下的所有part文件进行合并压缩的操作,合并完成后znode会消失
	queue: 这个目录下的内容是shard中各副本之间用于同步的队列,当同步完成后,队列也就会释放,znode会消失

可以通过下面这个命令获取zookeeper中当前路径下的znode数量(这个命令在3.7.0的zookeeper中存在,低版本可能没有这个命令)

[zk: localhost:2181(CONNECTED) 4] getAllChildrenNumber /path

通过上述命令可以查询到几乎所有的znode节点都来自于parts和queue节点中,也就是上面的第四层

zookeeper并不是用于存储数据的服务,znode的上限可能也就150万,目前系统该值已超过正常值20倍,优先降低这个值,可通过下面方案执行

1、将zookeeper的磁盘由机械盘改为读写性能更好的固态盘
2、大量存储znode节点,必定导致寻址时间边长,链接超时,修改一下参数
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
# This value is not quite motivated
initLimit=300
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=10

maxClientCnxns=2000
# It is the maximum value that client may request and the server will accept.
# It is Ok to have high maxSessionTimeout on server to allow clients to work with high session timeout if they want.
# But we request session timeout of 30 seconds by default (you can change it with session_timeout_ms in ClickHouse config).
maxSessionTimeout=60000000
#dataDir是数据目录,不配置dataLogDir的话,会将snapshot和事务log存储在一起,配置dataLogDir会将两者分开存储,若事务日志频繁,分开可降低磁盘IO
# the directory where the snapshot is stored.
dataDir=/opt/zookeeper/{{ '{{' }} cluster['name'] {{ '}}' }}/data
# Place the dataLogDir to a separate physical disc for better performance
dataLogDir=/opt/zookeeper/{{ '{{' }} cluster['name'] {{ '}}' }}/logs
#默认值为yes,用于配置zookeeper是否在事务提交的时候,将日志写入操作强制刷新磁盘,默认是yes,即每次事务日志写入操作都会实时刷入磁盘,如果是no,可以提高zookeeper写性能,存在事务日志丢失的风险
forceSync=no
#本地snapshot文件的保留个数和保留时长(单位:小时)
autopurge.snapRetainCount=10
autopurge.purgeInterval=1
# To avoid seeks ZooKeeper allocates space in the transaction log file in
# blocks of preAllocSize kilobytes. The default block size is 64M. One reason
# for changing the size of the blocks is to reduce the block size if snapshots
# are taken more often. (Also, see snapCount).
preAllocSize=131072
# Clients can submit requests faster than ZooKeeper can process them,
# especially if there are a lot of clients. To prevent ZooKeeper from running
# out of memory due to queued requests, ZooKeeper will throttle clients so that
# there is no more than globalOutstandingLimit outstanding requests in the
# system. The default limit is 1000.
# globalOutstandingLimit=1000

# ZooKeeper logs transactions to a transaction log. After snapCount transactions
# are written to a log file a snapshot is started and a new transaction log file
# is started. The default snapCount is 100000.
snapCount=3000000
# Leader accepts client connections. Default value is "yes". The leader machine
# coordinates updates. For higher update throughput at thes slight expense of
# read throughput the leader can be configured to not accept clients and focus
# on coordination.
leaderServes=yes
3、增加zookeeper节点,由3个变成5个,水平扩展后每个节点存储的znode数量一致,但是可分担请求,降低单个节点的压力
4、调整zookeeper的jvm内存,因管理的znode节点太多,4G内存肯定不够用了,会频繁报错,内存溢出,修改为30G

通过上述配置,基本可解决zookeeper连接超时和频繁选举问题

好的,那么现在来解决降低znode的问题,先来简单了解一下clickhouse集群,分片,副本的关系,具体可参考这个链接:clickhouse 副本与分片详解

A1+A2是一份完整数据,另一个相同的A1,A2则是当前数据的副本
在这里插入图片描述

通过sql可以查询当前clickhouse数据库的表状态,是否为只读状态,表的副本之间同步未完成时,表为只读
select database,table,replica_name,is_readonly,zookeeper_path,replica_path from clusterAllReplicas('ck_ax',system.replicas) where is_readonly=1

方案一
数据准备

找到parts数量⽐较⼤的表,记录下原始数据记录, 包括原始表的part数量,条数,⼤⼩,zookeeper上
对应的znode数量(关注该表的znode总数,replicas/parts, replicas/queue的znode数量)

#到所有的clickhouse节点上去执行这个语句
DETACH TABLE database.table PERMANENTLY SYNC
#校验detach之后的数据
1、/{clickhouse_data}/clickhouse/data/ckman_db_bob/{tablename}/detached/ 
2、⽬录下是否有数据zookeeper上该表的replicas/parts, replicas/queue 是否为空
3、该表的znode数量是否下降

!!!注意记录被detach的表名,因为表⼀旦被detach,⽆法通过show tables查看到。 

数据恢复

依然是到每一个clickhouse节点上执行这个操作
Attach TABLE database.table;
根据第⼀步记录的detach前数据,对⽐attach之后的数据,需保证条数⼀致,⼤⼩⼤约⼀致(数据⼤⼩有变化正常),
zookeeper上replicas/parts, replicas/queue需要有内容才算符合预期。

手动merge合并

OPTIMIZE TABLE ckman_db_bob.xxx FINAL;

数据校验

1、检查该表是否可查
2、检查该表的part数量是否下降
3、检查该表的znode数量是否下降

方案二

#到所有的clickhouse节点上去执行这个语句
DETACH TABLE database.table PERMANENTLY SYNC
#重新创建znode节点
deleteall /path/parts
deleteall /path/queue
create /path/parts
create /path/queue
#恢复表
Attach TABLE database.table;
#互相删除副本信息 192.168.10.101和192.168.10.102互为副本,则去101上删除102的信息,102上删除101的信息
192.168.10.101上执行
system drop replica '192.168.10.102' from table database.table;
192.168.10.102上执行
system drop replica '192.168.10.101' from table database.table;
#重新启动副本间的同步
system restart replica database.table
这个命令通常用于分布式表的环境中,当某个副本因为某种原因(如网络问题、节点故障等)而停止同步或变得不一致时,
可以使用这个命令来尝试重新启动该副本的同步过程。执行这个命令后,ClickHouse会尝试从集群中的其他副本恢复数据,
以确保该副本的数据与其他副本保持一致。这个命令不会删除或修改表中的数据,而是尝试恢复副本的同步状态
#重新同步数据,更新元数据信息
system restore replica database.table
当你执行 SYSTEM RESTORE REPLICA database.table时,ClickHouse 会尝试从集群中的其他正常工作的副本同步数据,
以恢复当前副本的数据状态。这个命令不会删除任何数据,而是会尝试从其他副本中拉取缺失或不一致的数据块,以修复当前副本的数据
#restart和restore的区别
restart只是重启同步,restore涉及副本之间的数据传输,并能同步更新zookeeper中的元数据信息

这次故障中znode数量在3100万,使用方案一已经晚了,只能采用极端的方案二,但是zookeeper中元数据请谨慎操作!!!

通过上述操作后,znode节点数量成功降低到750万,集群恢复

现在回到了原点,集群可用了,但是为什么一开始会报错

DB::Exception: Too many parts (300). Parts cleaning are processing significantly slower than inserts (version 21.4.6.55 (official build))

查询后发现个别表单表每日数据量在7T左右,插入后进行合并时,会出现一些大part,达到200GB,这些part在合并时无法merge,但合并的线程不释放,那么问题就找到了,线程被占用,其余的表无法合并,长此以往parts数量只会越来越多

使用sql可以查看clickhouse的系统表
show tables from system
select * from system.merges #可以查看merge的详情
每张系统表的字段和含义可查看官方文档

1、既然超过200GB无法合并,那么就将这个默认值调大,在磁盘空间有富余的情况下

max_bytes_to_merge_at_max_space_in_pool

The maximum total parts size (in bytes) to be merged into one part, if there are enough resources available. max_bytes_to_merge_at_max_space_in_pool -- roughly corresponds to the maximum possible part size created by an automatic background merge.

Possible values:

Any positive integer.
Default value: 161061273600 (150 GB).

The merge scheduler periodically analyzes the sizes and number of parts in partitions, and if there is enough free resources in the pool, it starts background merges. Merges occur until the total size of the source parts is larger than max_bytes_to_merge_at_max_space_in_pool.

2、上述配置治标不治本,即使调整到1T,也有可能会超过上限

1、每次插入产生part,调整加大单次插入数据量,则可以减少part
2、合理利用CPU,MEM对插入过程优化
3、merge是对单个partition做的,单个partition中part过多,可以多partition进行更细致的划分

参考文档:
https://blog.csdn.net/liudonglovehemin/article/details/130463392
https://blog.csdn.net/zxf126126/article/details/131119307
https://blog.csdn.net/u010280075/article/details/135713320?spm=1001.2014.3001.5502

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值