SQL CockroachDB Leaseholder 转移的 locality 优化配置实践

14次阅读

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

SQL CockroachDB Leaseholder 转移的 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=USEASTregion=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.userssystem.jobs 这类系统表不建议加 locality 约束,容易引发元数据访问抖动
  • 变更 lease_preferences 后,旧 leaseholder 不会立刻迁移,要等租约到期(默认 4.5s)或触发 cockroach node decommission 才强制切换

locality 优化不是开个开关就完事,它把分布式系统的权衡显性化了:你指定位置,就得承担位置带来的延迟或可用性代价。配置写错一行,监控里 latency p99 就开始跳,但错误日志里往往只有一行 lease transfer failed: not found,得顺着 gossip 和 zone config 两头查。

text=ZqhQzanResources