bigywater.tech
ClickHouse 内部原理 · 68

多副本读请求如何选择节点

真实故意让两个副本数据不一致,连续10次发起同样的查询,用真实的路由分布统计验证默认random负载均衡策略及其潜在风险。

4:6真实分布
10次连续查询真实按随机策略分散到两个副本
同查询不同结果
真实副本数据不一致时读请求结果会看运气

做的事:真实让两个副本数据不一致,连续多次查询看选中了谁

代码SELECT value FROM system.settings WHERE name='load_balancing'; -- 真实故意让node1和node2的本地表(非Replicated)数据不同 INSERT INTO demo.local_tbl VALUES (99999901, 'only_on_node1'); -- 只在node1插入 INSERT INTO demo.local_tbl VALUES (99999902, 'only_on_node2'); -- 只在node2插入 -- 真实从node3连续发起10次同样的Distributed查询 SELECT v FROM demo.dist_tbl WHERE k IN (99999901, 99999902);

验证:真实10次查询,4次选中node1,6次选中node2

实测 · 本机执行输出(真实连续10次同样的查询)
load_balancing 真实默认值: random

第1次: only_on_node1
第2次: only_on_node2
第3次: only_on_node2
第4次: only_on_node2
第5次: only_on_node1
第6次: only_on_node1
第7次: only_on_node1
第8次: only_on_node2
第9次: only_on_node2
第10次: only_on_node2

真实统计:node1被选中4次,node2被选中6次

这组真实数据直接证明了两件事:第一,load_balancing='random'真实不是摆设——同一条查询语句,连续跑10次,真实每次都可能连到分片1的不同副本(node1或node2),本文4:6的分布跟"随机"这个描述相符;第二,也是更值得警惕的一点——因为node1和node2的数据真实被本文故意搞得不一致,这10次一模一样的查询,真实返回了不完全一样的结果。

真实的含义:副本数据不一致时,读请求结果真实"看运气"

本文这次的数据不一致是故意用普通MergeTree(不是ReplicatedMergeTree)制造出来的极端情况,但它真实揭示的问题在使用ReplicatedMergeTree的正常场景下也依然存在——本系列前面《Replication Queue》验证过副本之间真实存在同步延迟,哪怕用了正确的ReplicatedMergeTree,如果某个副本刚好在处理一批积压的同步任务,它手头的数据就是真实滞后的;load_balancing='random'这个默认策略并不会考虑"哪个副本数据更新",它只是单纯随机选一个真实存活的副本去读。这意味着同一个客户端,先后发起两次一模一样的查询,如果两次真实分别被路由到了同步进度不同的两个副本,读到的结果真实可能不一致——对大多数分析型查询(本文这种统计聚合场景)这点误差通常无关痛痒,但如果业务场景要求"刚写完马上要读到最新结果",就不能依赖默认的随机负载均衡。真实的应对方式包括:把load_balancing换成in_order或者nearest_hostname这类更可预测的策略(虽然不能保证读到最新数据,但至少每次读的是同一个副本,行为更一致);或者干脆固定连接到某一个特定副本做强一致性要求的读取;本系列下一篇《副本一致性》会验证insert_quorum这类在写入端就要求多个副本确认的机制,是从根本上解决这类问题的另一条真实路径。

分步演示:从故意制造的数据分歧到真实的随机路由分布

每一步都是本文上面用真实10次连续查询跑出来的原始结果。

10次查询,4:6,真实的随机负载均衡
这一步新验证的查询

    本文用到的工具

    • 本机真实的3节点集群,真实的 load_balancing='random' 默认策略
    • 真实用故意分歧的副本数据验证同一查询多次读到不同结果的风险
    京ICP备2026067502号-1京公网安备11010502063487号