vtgate通过分片键匹配路由sql,未命中则广播;vttablet依赖vitess_schema同步表结构;vtcoord批量写入非原子;sql重写会丢失别名。

VTGate 怎么把 SQL 请求分发到正确的分片?
VTGate 不解析 SQL 语义,只做轻量级路由判断。它依赖表的 vitess_schema 元信息(比如 sharding_key 字段定义)和查询中的 WHERE 条件,匹配预定义的分片规则(如 hash、range)。没命中分片键的查询(比如 SELECT * FROM user WHERE status = 'active'),默认广播到所有分片。
- 分片键必须出现在
WHERE或IN子句中,且不能被函数包裹(WHERE YEAR(created_at) = 2024就不识别) -
JOIN跨分片表时,VTGate 会退化为“主表驱动 + 关联表广播”,性能陡降,实际应避免 - 如果用
IN,值数量超过queryserver-config-max-in-values(默认 1000),VTGate 会拒绝执行并报错in list too large
VTTablet 启动后查不到表结构?
VTTablet 本身不托管 DDL,它只加载本地 MySQL 实例已有的表结构。如果执行了 ALTER TABLE 但没同步更新 vitess_schema 中的 table_info,VTTablet 会认为该表“不存在”,导致 VTGate 路由失败或返回空结果。
- 必须通过
ApplySchema命令更新全局 schema:vitessctl ApplySchema --keyspace=commerce --sql="ALTER TABLE user ADD COLUMN tags JSON"
- 直接在底层 MySQL 执行 DDL 后,VTTablet 日志会出现
schema mismatch警告,但不会自动重载 - 多个分片的表结构不一致时,VTTablet 不报错,但 VTGate 可能因字段缺失抛出
column not found
为什么 VTCoord 的 ExecuteBatch 返回部分成功?
VTCoord 是 VTTablet 内部协调组件,负责处理批量写入。它默认不开启事务性批量(即不是原子的 START TRANSACTION; ... COMMIT),而是对每个 INSERT/UPDATE 单独提交。只要某一分片写入失败(比如唯一键冲突、主键不存在),其余分片仍会提交成功。
- 错误类型是
VtTabletError,常见错误码:2002(连接失败)、1062(唯一键冲突)、1364(字段无默认值) - 没有跨分片回滚机制,应用层需自行实现补偿逻辑(比如记录失败 ID 后异步重试)
- 若需强一致性,应改用单分片事务 +
Begin/Commit,或启用TwoPc(但会显著增加延迟和锁竞争)
SQL 被重写后字段别名丢失?
VTGate 在路由前会对 SQL 做语法树重写(例如把 SELECT id FROM user 改成 SELECT user.id FROM user 以适配分片表名),这个过程会抹掉原始别名。尤其在 GROUP BY 或 ORDER BY 引用别名时,会报错 Unknown column 'xxx' in 'order clause'。
- 别名必须显式重复在
ORDER BY中,不能依赖SELECT里的定义
✅ 正确:SELECT u.id AS uid FROM user u ORDER BY u.id
❌ 错误:SELECT u.id AS uid FROM user u ORDER BY uid - 使用
UNION时,VTGate 会统一列名,但各子查询的别名不继承,建议用位置序号(ORDER BY 1)代替 -
JSON_EXTRACT等函数返回的匿名字段,在重写后更易丢失上下文,建议始终用AS显式命名
分片键设计不合理、schema 同步滞后、以及过度依赖 SQL 别名——这三处最容易在上线后突然暴露,而且日志里往往只报错不报因。











