SQL Vitess 的 VTGate query routing 与 scatter/gather 性能优化

1次阅读

vtgate未走预期分片主因是sql含非vindex列的where条件或跨分片join,导致scatter/gather;仅where分片键=常量可单分片路由,in需全常量且数量≤max_in_count,vindex类型与配置错误亦会失效。

SQL Vitess 的 VTGate query routing 与 scatter/gather 性能优化

VTGate 的 query routing 为什么没走预期分片?

VTGate 默认按 Vindex 值路由,但只要 SQL 里出现非 Vindex 列的 WHERE 条件(比如 WHERE status = 'active'),它就无法确定目标分片,会退化为 scatter/gather。

  • 确认是否用了 Vindex 对应列:比如分片键是 user_id,那只有 WHERE user_id = 123 才能单分片路由
  • 避免在 WHERE 中混用非分片字段——哪怕加了索引也没用,VTGate 不查底层表结构
  • EXPLAIN 输出里看到 Route: Scatter 或多个 Keyspace: xxx, Shard: -80,80- 就是典型信号
  • 联合查询(JOIN)跨分片时,VTGate 一律 scatter/gather,不尝试重写或下推

scatter/gather 查询慢,怎么定位瓶颈?

慢不一定是网络或分片多,更可能是 VTGate 自身串行执行、结果合并开销大,或者下游 mysql 连接池打满。

  • 检查 vtgate 日志里的 QueryDurationScatterDuration 字段:前者长说明单分片慢,后者长说明并发调度/聚合耗时高
  • SHOW viteSS_STATUSActiveScatterQueries 是否持续高位,结合 MaxOpenConnections 配置判断连接池是否成为瓶颈
  • 避免在 scatter/gather 查询中 select 大字段(如 TEXTBLOB),VTGate 要在内存里拼整行再返回,OOM 风险陡增
  • 如果只是 COUNT 或 SUM,优先改用 SELECT SUM(col) FROM t GROUP BY 1 形式,让 VTGate 能做部分聚合,减少回传数据量

想强制单分片路由,但 INOR 让 VTGate 放弃优化

VTGate 对 IN 的处理很保守:只要列表长度 > 1,且不是完全由常量构成(比如含子查询、变量、函数),就会 fallback 到 scatter。

  • 安全写法只有一种:WHERE user_id IN (1001, 1002, 1003) —— 全常量、无表达式、数量别超 50(默认 max_in_count 限制)
  • WHERE user_id = ? OR user_id = ? 等价于 IN,同样受限制;用 union ALL 拆成多个单值查询反而更稳
  • 修改 vtgate 启动参数 --max_in_count=200 可放宽限制,但注意内存占用会上升,尤其配合 SELECT *
  • 不要依赖 HINT 强制路由:Vitess 目前不支持类似 MySQL 的 /*+ USE_INDEX */ 语法控制分片选择

vtctlclient 查路由策略,但输出看不懂

vtctlclient GetRoutingRulesGetVSchema 返回的是原始 json,关键信息藏得深,容易误读分片逻辑。

  • 重点盯 vindexes 下的 type 字段:unicode_loose_md5 是哈希分片,lookup 是映射表,binary 是范围分片——类型不同,路由行为差异极大
  • tables 里每个表的 column_vindex 必须和 vindexes 名称严格匹配,大小写敏感,配错就等于没分片键
  • 执行 vtctlclient ValidateVSchema 能发现大部分配置矛盾,比如分片键字段不存在、Vindex 类型不支持该字段类型等
  • 别信 vtctlclient GetSrvKeyspaceShards 列表——它只显示拓扑,不反映当前查询是否真能落到这些 shard 上

分片键设计一旦上线就很难改,Vindex 类型选错、字段类型不匹配、或早期没预留足够 shard 数量,都会让后续所有 query routing 优化失效。这些不是配置问题,是架构决策点,调参绕不过去。

text=ZqhQzanResources