<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>ReplicatedMergeTree on </title>
    <link>/tags/replicatedmergetree/</link>
    <description>Recent content in ReplicatedMergeTree on </description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Wed, 14 Oct 2026 21:40:00 +0800</lastBuildDate><atom:link href="/tags/replicatedmergetree/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>ZooKeeper 顺序节点计数器溢出导致 ClickHouse 复制表无法写入</title>
      <link>/posts/clickhouse-zookeeper-sequence-overflow/</link>
      <pubDate>Wed, 14 Oct 2026 21:40:00 +0800</pubDate>
      
      <guid>/posts/clickhouse-zookeeper-sequence-overflow/</guid>
      <description>复制表的 INSERT 某一刻起全部失败，三个副本报同一个错，读不受影响：
Code: 244. DB::Exception: Unexpected logical error while adding block 0 with ID &amp;#39;…&amp;#39;: Node exists, path /clickhouse/tables/&amp;lt;uuid&amp;gt;/01/log/log-. (UNEXPECTED_ZOOKEEPER_ERROR) 原因是 ZooKeeper 里给复制日志编号的计数器到了 int32 上限。这句报错出自 clickhouse-lab 的实验 28：在 ClickHouse 25.3.14.14 和 Apache ZooKeeper 3.8.4 上，把一张复制表的计数器推到上限，复现了这个问题，也验证了只用 SQL 就能恢复。
失败的是在 log/ 下建复制日志那一步 ClickHouse 提交一个 part 时，把几件事放进同一个 ZooKeeper 事务：登记这个 part、记下去重用的块哈希、在 log/ 下建一条复制日志（ReplicatedMergeTreeSink.cpp:865）。报错的 path 指向 log/log-，说明失败的是建复制日志这一件。log/ 下的条目是顺序节点，ClickHouse 只给前缀 log-，序号由 ZooKeeper 追加，按理不会重名。log/ 里放的是什么、另外两个副本怎么照着它拉 part，写入路径那篇写过。
ZooKeeper 的序号来自父节点上的一个 int32 ZooKeeper 的文档说，这个序号来自父节点维护的一个有符号 4 字节整数，「the counter will overflow when incremented beyond 2147483647」（ZooKeeper Programmer&amp;rsquo;s Guide）。3.</description>
    </item>
    
    <item>
      <title>ClickHouse 的写入路径</title>
      <link>/posts/clickhouse-write-path/</link>
      <pubDate>Sun, 11 Oct 2026 08:00:00 +0800</pubDate>
      
      <guid>/posts/clickhouse-write-path/</guid>
      <description>背景 这个系列里的交易明细从 Kafka 经 ClickHouse 官方的 Kafka Connect sink 写进一套 1 shard × 3 replicas 的集群。sink 每次 poll 按 Kafka 分区各发一条 INSERT，一条往往只有十几到几十行。这套系统按 region 分成几套独立部署，数据量最大的两套一天分别约两亿行和三亿多行：两亿行那套三个副本合计每秒新建约 124 个写入块，每块十几到二十行；三亿多行那套每秒约 40 个块，每块约 90 行。
ClickHouse 不原地改数据。每次写入生成一个新的 part，写完就不再动，后台再把小 part 合并成大 part。后面几篇反复用到这条规则：重复为什么会直接落库，去重为什么要等合并，改一行数据为什么要给整个 part 出一个新版本，修数据为什么按分区整体替换。这篇按一条 INSERT 的顺序把写入路径过一遍。
版本以 25.3 为准，Aiven 上的现有集群是 25.3.14.1。文中「实验 NN」指 clickhouse-lab 里 experiments/ 下的同号脚本，输出在 results/，跑在一套 25.3.14.14 的三副本实验台上。
一条 INSERT 怎么变成 part 服务端收到一批行，先按分区键把行拆开，每个分区单独成一块；每块按排序键排序、拆成列、逐列压缩，写进一个新目录，这个目录就是 part，稀疏主键索引也写在里面（Parts）。复制表在提交前还要去 Keeper（存复制元数据的协调服务，Aiven 在每个节点上同机跑一个 ZooKeeper）领块号、查重、记一条复制日志，这几步放在后面复制和块级去重两节。
lab 上新写的一个 Wide part，目录里是这些文件：
20260310_0_0_0/ 分区 ID_起始块号_结束块号_合并层级 k.bin k.</description>
    </item>
    
    <item>
      <title>ClickHouse 的块级去重窗口</title>
      <link>/posts/clickhouse-block-dedup-window/</link>
      <pubDate>Wed, 16 Sep 2026 20:00:00 +0800</pubDate>
      
      <guid>/posts/clickhouse-block-dedup-window/</guid>
      <description>一条 Kafka Connect sink 往 ReplicatedMergeTree 写数据，INSERT 卡过 30 秒超时之后框架把同一批原样重投，服务端两次都提交了，表里就多出 234 行除写入时间外逐字节相同的数据（这条链路怎么走到重投，写在ClickHouse 里的重复行来自 Kafka Connect 超时重投）。
ReplicatedMergeTree 自带块级去重（block-level deduplication），服务端记住最近若干个插入块的标识（有 token 用 token，没有就用块哈希），重复的块直接跳过。ClickHouse 团队 2024 年发表的论文说，这层去重就是为超时重发设计的：客户端遇到超时，分不清写没写进去，就把同一批原样再发一遍，交给服务端去重（3.5 节）。这张表本来就是 Replicated，这层默认是开着的；重投出去的记录、顺序、边界和第一次一模一样，插件给 INSERT 带的 insert_deduplication_token 也没变。认得出来的条件都在，它还是没拦住，因为这个窗口按块数算，而这张表建块太快。
窗口按块数算，默认 1000 个块 查一下生效值：
SELECT name, value FROM system.merge_tree_settings WHERE name IN (&amp;#39;replicated_deduplication_window&amp;#39;, &amp;#39;replicated_deduplication_window_seconds&amp;#39;) replicated_deduplication_window 1000 replicated_deduplication_window_seconds 604800 窗口是 1000 个块，_seconds 是一周。这两个值没人改过，就是 25.9 之前的上游默认值。查询里写两个具体的名字，是因为 25.3 上用 LIKE &#39;%dedup%&#39; 会多出四行别的设置。
上游后来把两个值都动了：25.9 起 replicated_deduplication_window 从 1000 提到 10000（PR #86820），25.10 起 replicated_deduplication_window_seconds 从一周改成一小时（PR #87414，标了 backward incompatible）。Aiven 上的现有集群跑的是 25.</description>
    </item>
    
  </channel>
</rss>
