
如何为 ClickHouse 插入数据设置去重令牌避免重试产生重复行【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse插入操作可能因超时等错误失败失败时数据“可能已插入、也可能没有插入”客户端通常只能重试整条INSERT。ClickHouse 为这类重试提供了基于block_id的插入去重每个数据块被分配一个block_id块内数据的哈希重试时若同一个block_id已在目标表或依赖它的物化视图的去重日志中ClickHouse 不会再次插入该块但客户端仍会收到成功状态如同数据正常插入一样。block_id只按数据计算在两种情况下帮不上忙你有意多次插入完全相同的数据第二次会被当成重试丢掉以及数据里含有每次执行都不同的列例如createdAt DateTime64(3) DEFAULT now()每行都会生成唯一值重试的块哈希对不上去重永远不会触发。这两种情况都靠同一个设置解决insert_deduplication_token即本标题所说的去重令牌。它让用户为每次插入指定一个唯一字符串ClickHouse 用它而不是数据哈希来判断是否重复——提供令牌后数据哈希完全不参与比较。去重生效的两个前提表级日志 查询级开关ClickHouse 只有同时满足以下两点才执行插入去重缺一不可目标表保留去重日志表级设置查询启用了去重查询级设置。表级确认目标表有去重日志只有*MergeTree引擎支持插入去重其他引擎不适用。非复制的*MergeTree去重日志由non_replicated_deduplication_window控制默认值为0即默认不记录任何哈希、不去重任何数据。普通MergeTree表必须把它设成正值才有去重能力该值表示“保留最近多少已插入块的哈希和”。哈希和写入本地磁盘文件。*ReplicatedMergeTree去重日志默认开启由replicated_deduplication_window默认10000个块和replicated_deduplication_window_seconds默认3600秒控制哈希和写入 ClickHouse Keeper。窗口越大比较越慢、插入越慢。在建表或已有表上开启非复制表的去重日志CREATE TABLE dst ( key Int64, value String ) ENGINE MergeTree ORDER BY tuple() SETTINGS non_replicated_deduplication_window 1000;查询级确认deduplicate_insert为启用状态deduplicate_insert是插入去重的主开关作用于同步和异步插入取值为enable— 启用去重disable— 禁用去重backward_compatible_choice— 交由旧设置insert_deduplicate同步/async_insert_deduplicate异步决定。自版本 26.2 起默认值为enable。因此insert_deduplicate 0单独已经关不掉去重要禁用必须显式写deduplicate_insert disable。如果你的会话设置了compatibility为早于26.2的版本deduplicate_insert会退回旧默认backward_compatible_choice此时去重与否由insert_deduplicate/async_insert_deduplicate决定显式赋值的设置始终生效不受compatibility影响。另一个必须记住的不对称行为以deduplicate_insert disable执行的查询不会为任何块写入block_id这些数据之后即使改用enable重试也无法被去重目标表没有去重日志时同理——没记录就无可比对。设置去重令牌insert_deduplication_tokeninsert_deduplication_token接受任意字符串只在非空时参与去重。它优先级高于数据哈希提供令牌后ClickHouse 完全不用数据哈希来判断重复。令牌按分区partition跟踪同一次插入写入多个分区时可以携带同一令牌。在INSERT语句中通过SETTINGS子句设置。官方设置文档中的完整示例CREATE TABLE test_table ( A Int64 ) ENGINE MergeTree ORDER BY A SETTINGS non_replicated_deduplication_window 100; INSERT INTO test_table SETTINGS insert_deduplication_token test VALUES (1); -- 下一条不会被去重因为令牌不同 INSERT INTO test_table SETTINGS insert_deduplication_token test1 VALUES (1); -- 下一条会被去重因为令牌与前面某条相同 INSERT INTO test_table SETTINGS insert_deduplication_token test VALUES (2); SELECT * FROM test_table文档示例结果注意令牌相同的第三条插入数据是(2)与第一条的(1)不同但因令牌相同仍被去重┌─A─┐ │ 1 │ └───┘ ┌─A─┐ │ 1 │ └───┘这个示例同时演示了令牌的核心语义一旦某个令牌被记录后续携带该令牌的插入无论数据是什么都会被当成重复而丢弃。如果你重试时源数据恰好变了两次尝试之间表被更新过无令牌时按数据计算会得到不同block_id重试会把新数据插到第一次写入的数据之上有令牌时重试仍被识别为重复并丢弃即使它本该插入不同的数据。两种行为都要自己选择令牌路径适合“同一个批次的重试”语义数据哈希路径适合“数据变了就写进去”的语义。重试时注意INSERT ... VALUES的块切分是确定性的、由相关设置决定所以重试必须与首次操作使用相同的设置值否则块边界不同、block_id对不上。验证去重是否生效文档中的验证方式就是查询目标表并观察 part 与行数是否增长。例如插入去重重试指南中的场景CREATE TABLE dst ( key Int64, value String ) ENGINE MergeTree ORDER BY tuple() SETTINGS non_replicated_deduplication_window1000; SET max_block_size1; SET min_insert_block_size_rows0; SET min_insert_block_size_bytes0;上述SET让一次插入产生多个单行块用来构造“同一次插入里有内容完全相同的两个块”的边界情况。不设置令牌时第二次出现的相同块会被按block_id去重丢掉文档明确说这不是期望行为正是令牌要解决的场景INSERT INTO dst SELECT 0 AS key, A AS value FROM numbers(2) SETTINGS insert_deduplication_tokensome_user_token; SELECT from dst, *, _part FROM dst ORDER BY all;文档示例结果——两个相同块按预期都写入┌─from dst─┬─key─┬─value─┬─_part─────┐ │ from dst │ 0 │ A │ all_2_2_0 │ │ from dst │ 0 │ A │ all_3_3_0 │ └────────────┴─────┴───────┴───────────┘随后用同一令牌重试第三次还故意换了数据(1, b)两次都因令牌相同被去重表中保持两行不变——这就是“设置令牌后如何判断重试被识别”的标准核对方式重放插入后行数与 part 集合不增长。异步插入场景另有计数指标system.events中的DuplicatedAsyncInserts与SelfDuplicatedAsyncInserts事件统计“批内某条查询是重复而被剔除”以及“同批两条查询携带同一令牌、第二条被丢弃”两种情况。INSERT ... SELECT与异步插入下的令牌INSERT ... SELECT去重要求SELECT部分每次尝试返回相同数据、相同顺序否则块不同、block_id不同重试不会被识别为重复。ClickHouse 无法验证源数据没变只能检查查询本身是否稳定查询带ORDER BY ALL子句只识别字面ORDER BY ALL普通的ORDER BY 表达式不算UNION多个SELECT永远不稳定且读取管线以单个 stream 结束。非空的insert_deduplication_token是稳定性的等价替代——令牌代替数据标识这次插入此时SELECT是否稳定都不影响去重。相关开关是deduplicate_insert_select默认enable_when_possible稳定或设置了令牌则去重否则跳过去重并写服务器日志设为force_enable时不稳定且无令牌会抛DEDUPLICATION_IS_NOT_POSSIBLE异常。所以对不稳定的INSERT ... SELECT做可靠重试正确做法就是带上insert_deduplication_token。异步插入自 26.2 起async_insert默认启用异步插入与同步插入共享同一条去重日志deduplicate_insert同时控制两者重试在两种模式间发送也互认重复。批处理粒度是“按用户查询”而不是按批每条排队的查询向批次贡献一个去重令牌——如果查询提供了insert_deduplication_token就用它的值否则用该查询所贡献行的哈希。批内一条查询是重复时只剔除它自己的行整批其余行正常插入part 只有在所有行都被剔除时才整体跳过。限制与边界插入状态不确定必须重试直到成功。如果所有重试都失败无法判断数据到底插入没有涉及物化视图时更无法确定数据可能出现在哪些表视图可能与源表失去同步。去重窗口限制重试序列期间如果发生了超过*_deduplication_window的其他插入操作去重可能失效同一数据会被插入多次插入大数据量时块数也可能溢出日志窗口。物化视图依赖视图的去重目标表需要deduplicate_blocks_in_dependent_materialized_views也允许去重26.2 起默认启用与deduplicate_insert二者都要放行源表和视图目标表才都能去重。令牌语义提供令牌后数据哈希不参与判断见上文第三次尝试示例重试即使携带不同数据也会被丢弃这是文档明确的取舍不是缺陷。行级去重例如 upsert 场景中同排序键保留最后一行是另一套机制——ReplacingMergeTree等表引擎在 merge 期间移除重复行与本文的插入去重令牌解决的不是同一问题参见去重策略文档。更多细节参见 Deduplicating inserts on retries 与insert_deduplication_token设置参考。【免费下载链接】ClickHouseClickHouse® is a real-time analytics database management system项目地址: https://gitcode.com/GitHub_Trending/cli/ClickHouse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考