leaseholder 不会自动按 locality 迁移,除非显式配置 lease_preferences;它仅依据节点健康度和 raft 状态选举,需通过 alter table ... configure zone 设置 lease_preferences 并确保 locality 标签一致、副本已分布对应区域。

Leaseholder 不会自动按 locality 迁移,除非显式配置
默认情况下,CockroachDB 的 leaseholder 选举只看节点健康度和 Raft 状态,完全不感知 locality。哪怕你给节点加了 --locality=region=us-east,datacenter=dc1,只要集群没配策略,读请求照样可能落到跨 region 的副本上,延迟直接翻倍。
真正起作用的是 ALTER TABLE ... CONFIGURE ZONE 中的 lease_preferences,它才是控制 leaseholder 落点的开关:
ALTER TABLE orders CONFIGURE ZONE USING lease_preferences = '[[+region=us-east], [+region=us-west]]';
-
lease_preferences是优先级列表,每项是 key-value 标签组合;第一组匹配成功的节点才可能成为 leaseholder - 必须用双中括号
[[...], [...]],单层或漏括号会导致语法错误invalid lease preference syntax - 标签值要和启动时
--locality完全一致(包括大小写、连字符),region=USEAST和region=us-east视为不同
region 级 locality 配置容易忽略节点标签一致性
常见错误是:集群初始化时部分节点用了 --locality=region=us-east,另一些却写成 --locality=region=useast 或漏掉 --locality。结果 lease_preferences 查不到匹配节点,leaseholder 就退化到随机选举——看起来“配置生效了”,其实根本没走 locality 路由。
验证方法很简单,查系统表:
SELECT node_id, locality FROM crdb_internal.gossip_nodes WHERE locality != '';
- 确保所有目标节点的
locality字段都包含预期键值对,且格式统一 - 如果某节点显示
locality: "",说明它根本没带--locality启动,重启时必须补上 - 修改
--locality后必须滚动重启,热更新不生效
lease_preferences 生效需要副本已分布在对应 locality
lease_preferences 只决定 leaseholder 归属,不负责数据副本分布。如果某个 region=us-east 下只有 1 个副本,而 lease_preferences = '[[+region=us-east]]',那 leaseholder 确实会落过去;但一旦该节点宕机,新 leaseholder 只能在剩余副本里选——哪怕它们全在 us-west,也会强行选,因为没得挑。
所以必须先保证副本分布满足 locality 约束:
ALTER TABLE orders CONFIGURE ZONE USING num_replicas = 3, constraints = '[+region=us-east], [+region=us-west], [+region=eu-west]';
-
constraints控制副本物理位置,lease_preferences控制读主节点,二者缺一不可 - 约束数量不能超过实际节点数,否则
SHOW ZONE CONFIGURATION FOR TABLE orders会报insufficient nodes to satisfy constraints - 新增节点后,副本不会自动 rebalance,需手动触发:
ALTER RANGE default REBALANCE
跨 region 写入场景下,leaseholder 强制 locality 可能抬高延迟
如果你的应用写请求主要来自 us-west,但把 lease_preferences 锁死在 us-east,那每次写都要跨 region 走 Raft 共识——30ms 延迟变 80ms,吞吐掉一半。这不是 bug,是设计使然。
真实业务往往需要权衡:读多写少,就倾向读本地;写密集,就得让 leaseholder 靠近写入口。这时可考虑动态策略:
- 按业务模块拆表,高频写表用
[[+region=client-region]],报表类只读表用[[+region=analytics-region]] - 避免全局统一配置,
system.users或system.jobs这类系统表不建议加 locality 约束,容易引发元数据访问抖动 - 变更
lease_preferences后,旧 leaseholder 不会立刻迁移,要等租约到期(默认 4.5s)或触发cockroach node decommission才强制切换
locality 优化不是开个开关就完事,它把分布式系统的权衡显性化了:你指定位置,就得承担位置带来的延迟或可用性代价。配置写错一行,监控里 latency p99 就开始跳,但错误日志里往往只有一行 lease transfer failed: not found,得顺着 gossip 和 zone config 两头查。










