分布式数据库适配方案¶
0. 耦合点流程图与清单¶
0.1 整体流程图¶
┌─────────────────────────────────────────────────────────────────┐
│ config.ini: db_mode = centralized | distributed │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ TDSQLClient.__init__() │
│ 读取 db_mode → self.db_mode │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌──────────────────┴──────────────────┐
│ ★ 分支点 1:execute_explain() │
│ │
├─ centralized ──► EXPLAIN ANALYZE │
└─ distributed ──► EXPLAIN │
└──────────────────┬──────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ node_load_inputs() │
│ ★ 分支点 2:构建 distributed_context │
│ │
│ ┌─ centralized ─► distributed_context = {} │
│ │ │
│ └─ distributed ─► build_distributed_context() │
│ │ │
│ ├─ analyze_distributed_explain() │
│ │ └─ 正则扫描 Exchange/Shuffle/Gather/... │
│ │ │
│ └─ load_table_distributed_metadata() │
│ ├─ 解析 ddl.sql (shardkey/PARTITION BY/BROADCAST) │
│ ├─ 解析 partitions.json │
│ └─ 读取 distributed.json (手动覆盖) │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ V3State {db_mode, distributed_context, ...} │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ node_build_prompt() → build_prompt_round1() │
│ ★ 分支点 3:模板变量替换 │
│ │
│ {db_arch_label} = centralized → "TDSQL(集中式)" │
│ = distributed → "TDSQL(分布式)" │
│ {db_mode} = centralized | distributed │
│ {distributed_context} = centralized → "" │
│ = distributed → JSON 串 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Prompt 模板 opt_v3.3.0.txt │
│ ★ 分支点 4:上下文段落 │
│ │
│ ┌─ 集中式 ──► distributed_context 段为空 │
│ └─ 分布式 ──► 显示分片键、EXPLAIN 算子、missing_info │
└────────────────────────────┬────────────────────────────────────┘
│
╔═══════════════════════════╧═══════════════════════════════╗
║ 共享路径(零耦合) ║
║ ║
║ ┌─────────────────────────────────────────────────────┐ ║
║ │ node_call_llm() ── 调 LLM,与 db_mode 无关 │ ║
║ └─────────────────────────────────────────────────────┘ ║
║ ▼ ║
║ ┌─────────────────────────────────────────────────────┐ ║
║ │ node_parse_llm() ── 解析 JSON,与 db_mode 无关 │ ║
║ └─────────────────────────────────────────────────────┘ ║
║ ▼ ║
║ ┌─────────────────────────────────────────────────────┐ ║
║ │ node_persist() ── 落盘 JSON,与 db_mode 无关 │ ║
║ └─────────────────────────────────────────────────────┘ ║
║ ▼ ║
║ ┌─────────────────────────────────────────────────────┐ ║
║ │ node_execute() ── 走 TDSQLClient(已在分支点 1) │ ║
║ └─────────────────────────────────────────────────────┘ ║
║ ▼ ║
║ ┌─────────────────────────────────────────────────────┐ ║
║ │ build_execution_feedback() ── 纯数值比较,无关 │ ║
║ └─────────────────────────────────────────────────────┘ ║
║ ▼ ║
║ ┌─────────────────────────────────────────────────────┐ ║
║ │ node_stop() ── 停止策略,与 db_mode 无关 │ ║
║ └─────────────────────────────────────────────────────┘ ║
╚═════════════════════════════════════════════════════════════╝
0.2 耦合点清单¶
| 分支点 | 位置 | 触发条件 | 现状 |
|---|---|---|---|
| 1. EXPLAIN 命令 | TDSQLClient.execute_explain() |
db_mode == "distributed" |
已实现 |
| 2. distributed_context 构建 | node_load_inputs() |
db_mode == "distributed" |
待实现 |
| 3. Prompt 变量替换 | build_prompt_round1() |
db_mode 值 |
待实现 |
| 4. Prompt 模板段落 | opt_v3.3.0.txt |
{distributed_context} 是否为空 |
待实现 |
0.3 关键观察¶
只有 4 个耦合点,全部集中在流程前端(输入收集 + Prompt 构建)。
从 node_call_llm 往后的所有节点(LLM 调用、解析、持久化、执行、反馈、停止判断)完全共享代码路径,与 db_mode 无关。这意味着:
- 集中式回归测试覆盖了 80%+ 的共享路径
- 分布式只增加 4 个 if 分支,风险可控
- 改动影响范围收敛,不需要全流程测试
主要风险点在分支点 2(DDL 解析),因为 TDSQL 的分片语法不是标准 SQL,正则识别容易漏判。建议这个分支点加 fallback 到 missing_info,不要因解析失败阻塞流程。
1. 现有入口与缺口¶
1.1 现有入口¶
| 能力 | 当前位置 | 状态 |
|---|---|---|
| 数据库模式配置 | config.ini.example 的 db_mode = centralized/distributed |
已有入口 |
| 分布式 EXPLAIN 分支 | core/tdsql_client/client.py:execute_explain() |
distributed 使用 EXPLAIN |
| 表上下文采集 | core/dataprocess_v3/table_ctx/* |
已采集 DDL、索引、分区、统计信息 |
1.2 当前缺口¶
- Prompt 顶部仍写死集中式描述
- 缺少
distributed_context结构 - 缺少分片键 / 路由信息合并逻辑
- 缺少分布式 EXPLAIN 算子结构化识别
2. 收集的信息清单¶
2.1 表级信息(每张涉及的表)¶
| 信息项 | 用途 | 来源优先级 |
|---|---|---|
| 分片键 shard_keys | 判断是否单分片查询、Join 是否对齐 | DDL > partitions.json > 手动补充 |
| 分区方式 partition_method | 区分 HASH / RANGE / LIST | partitions.json |
| 分区表达式 partition_expression | 辅助判断分片键 | partitions.json |
| 分区数 partition_count | 评估扫描规模 | partitions.json |
| 表行数 table_rows | 评估表规模 | 复用现有 table_stats.json |
| 是否广播表 is_broadcast_table | 识别广播表避免误判 | DDL 中的 BROADCAST 关键字 |
| 索引作用域 index_scope | 区分局部/全局索引 | DDL 中的 GLOBAL INDEX 关键字;无法判断时标记 local_unknown |
2.2 SQL 级信息(针对目标 SQL)¶
| 信息项 | 用途 | 来源 |
|---|---|---|
| EXPLAIN 分布式算子 operators | 判断跨分片执行形态 | EXPLAIN 文本扫描 |
| 是否跨分片 has_cross_shard | 风险标记 | 算子集合推断 |
| 是否全分片扫描 has_full_shard_scan | 风险标记 | 算子集合推断 |
| 是否广播 has_broadcast | 辅助理解执行策略 | 算子集合推断 |
| 执行证据 raw_evidence | 给 LLM 看的原文片段 | EXPLAIN 文本 |
2.3 EXPLAIN 扫描识别的算子¶
| 算子类别 | 关键词 | 含义 |
|---|---|---|
| Exchange / Shuffle | Exchange, Shuffle, HashShuffle, Redistribute | 跨节点数据重分布 |
| Gather / Merge | Gather, Gather Merge, Merge | 多分片结果汇总 |
| Broadcast | Broadcast | 小表广播 |
| Motion | Motion, Redistribute Motion, Broadcast Motion | PostgreSQL 系数据移动 |
| Scatter | Scatter, all shards, full shard | 全分片扫描 |
| TDSQL HashQuery | QueryMode: Hash |
proxy Hash 模式组织查询 |
| TDSQL ProxyDeduplicate | ProxyDeduplicate | proxy 层去重/聚合 |
| TDSQL SetRewrite | set_数字_数字 |
proxy 拆解/改写为中间集合 |
2.4 DDL 关键字识别规则¶
| DDL 关键字 | 提取目标 | 说明 |
|---|---|---|
shardkey=col / SHARD KEY (col) |
shard_keys | TDSQL 私有语法 |
PARTITION BY HASH(col) |
shard_keys, partition_method, partition_expression | MySQL 原生分区,可能是分片键 |
PARTITION BY RANGE/LIST(...) |
partition_method, partition_expression | 通常是分区不是分片 |
BROADCAST |
is_broadcast_table=true | TDSQL 广播表 |
GLOBAL INDEX |
index_scope=global | 全局索引 |
LOCAL INDEX / 无标记 |
index_scope=local_unknown | 需 DBA 确认默认值 |
2.5 信息缺失处理¶
无法识别的字段必须落入 missing_info,不要静默失败:
- 分片键无法识别 →
missing_info记录"缺少 {table} 的分片键" - 索引作用域无法判断 →
missing_info记录"无法判断 {table} 索引作用域" information_schema.partitions返回 NULL ≠ 没有分片键,只是未暴露
2.6 采集范围收敛原则¶
- 默认只采集当前 SQL 引用的表,不做全库扫描
- 表数
<= 5时全采;> 5时只采 Top 5(驱动表、最大事实表、Join 两侧大表、WHERE 过滤最多的表) - 小码表、字典表后置,缺分片信息不阻塞主表判断
- 表行数复用现有
table_stats.json,不重复查询
3. 信息采集命令¶
目标数据库已按分布式库处理,本节只保留表级和 SQL 级证据采集命令。
3.1 每类检查的用处¶
| 检查项 | 主要用处 | 能证明什么 | 不能证明什么 | 优先级 |
|---|---|---|---|---|
information_schema.partitions |
查看 MySQL 原生分区信息和表行数估计 | 是否暴露 PARTITION_METHOD、PARTITION_EXPRESSION;辅助判断表规模 |
TDSQL 分片键可能不在这里暴露;返回 NULL 不能说明没有分片 | 辅助 |
SHOW CREATE TABLE |
确认表结构、索引定义、分片语法 | 是否有 shardkey、SHARD KEY、PARTITION BY、GLOBAL INDEX;是判断分片键的最关键证据 |
厂商隐藏分片元数据时 DDL 也看不到 | 必查 |
SHOW INDEX FROM |
查看已有索引列顺序和基数 | 当前是否已有可用索引;是否可能重复建索引 | 通常看不出是否跨分片;不一定能看出局部/全局作用域 | 建议查 |
目标 SQL 的整体 EXPLAIN |
判断 SQL 实际执行形态 | 是否出现 QueryMode: Hash、ProxyDeduplicate、set_...、Exchange、Shuffle 等分布式执行证据 |
不能直接给出表的分片键;局部输出可能漏掉关键算子 | 必查 |
单表过滤 EXPLAIN |
判断单表访问路径 | WHERE 是否命中索引;type/key/rows/filtered 是否健康 |
不能证明单分片或跨分片;不能替代整体 EXPLAIN | 辅助 |
3.2 使用原则¶
- 写方案阶段不需要补齐所有命令输出,只记录需要采集的证据和缺失项
- 真正评估某条 SQL 时,优先拿"整体 EXPLAIN + 关键表 SHOW CREATE TABLE"
- 单表 EXPLAIN 只用于解释索引命中,不用于判断分布式安全性
- 表很多时不要全查,只查主表、大表、Join 两侧表;其余表写入
missing_info
3.3 必查命令¶
目标表 DDL 和分片键:
SHOW CREATE TABLE db_name.table_name\G
重点看 shardkey、SHARD KEY、PARTITION BY、DISTRIBUTED BY、BROADCAST、GLOBAL INDEX。表多时先查数据量最大的 2-3 张事实表。
分区/分片表达式:
SELECT table_schema, table_name, partition_name,
partition_method, partition_expression, table_rows
FROM information_schema.partitions
WHERE table_schema = 'db_name'
AND table_name IN ('table1', 'table2')
ORDER BY table_name, partition_ordinal_position;
目标 SQL 是否跨分片:
EXPLAIN SELECT ...;
输出重点保留含以下关键词的行:Exchange、Shuffle、Gather、Broadcast、Motion、Scatter、QueryMode、ProxyDeduplicate、set_、remote、all shard、partition。
4. 工作流接入方向¶
4.1 配置层¶
config.ini 中分布式库新增 db_mode = distributed 配置项(已有入口,无需改动)。
4.2 State 层¶
V3State 新增两个字段:
- db_mode: 标识当前数据库模式
- distributed_context: 存储采集到的分布式上下文(集中式为空)
4.3 输入加载层¶
node_load_inputs() 在 db_mode == "distributed" 时触发分布式上下文采集:
- 扫描目标 SQL 的 EXPLAIN 文本,识别分布式算子
- 对涉及的表,从 table_ctx/ 目录加载分布式元数据(分片键、广播表、索引作用域)
- 把采集结果写入 distributed_context,落入 missing_info 的项不阻塞流程
4.4 Prompt 构建层¶
build_prompt_round1() 在 db_mode == "distributed" 时把分布式上下文注入 Prompt:
- 数据库架构标签动态切换(集中式 ↔ 分布式)
- 在列选择性之后追加分布式上下文段(集中式时该段为空)
- 不改变现有 Prompt 的其他结构
4.5 共享路径(无改动)¶
以下节点与 db_mode 完全无关,不需要任何改动:
| 节点 | 不改动原因 |
|---|---|
node_call_llm() |
LLM 调用与架构无关 |
node_parse_llm() |
JSON 解析与架构无关 |
node_persist() |
落盘逻辑与架构无关 |
node_execute() |
已走 TDSQLClient(分支点 1 已处理) |
build_execution_feedback() |
纯数值比较 |
node_stop() |
停止策略与架构无关 |
5. 实施计划¶
Phase 1:最小可落地¶
目标:跑通一条分布式 SQL 的端到端优化,Prompt 中能看到分布式上下文。
主要工作:
- 新增独立模块承载分布式上下文采集逻辑(EXPLAIN 扫描 + 表元数据合并)
- State 层加 db_mode、distributed_context 两个字段
- node_load_inputs() 中按 db_mode 分支构建上下文
- Prompt 模板增加 {db_mode}、{db_arch_label}、{distributed_context} 占位符
- 集中式路径行为零变化(所有改动在 db_mode == "distributed" 分支内)
Phase 2:DDL 解析增强¶
目标:自动识别主流分布式 DDL 语法,减少对 DBA 手动补充的依赖。
主要工作:
- TDSQL shardkey / SHARD KEY 语法识别
- MySQL 原生 PARTITION BY HASH 语法识别(区分 RANGE/LIST)
- BROADCAST 广播表识别
- GLOBAL INDEX 全局索引识别
- distributed.json 手动覆盖机制(DBA 优先级最高)
验证标准¶
- 集中式路径(
db_mode=centralized)行为零变化,回归测试全部通过 - 分布式路径下 Prompt 中能看到
distributed_context missing_info能正确反映未采集到的表和字段- DDL 解析能识别至少 3 种分片语法(TDSQL shardkey、MySQL PARTITION BY HASH、BROADCAST)
风险点¶
| 风险 | 影响 | 缓解 |
|---|---|---|
| TDSQL DDL 样本缺失 | 正则写不对 | 先跑测试 SQL 拿真实 DDL,再写解析 |
| Prompt 模板改动影响 LLM 输出质量 | 优化效果回退 | 集中式回归 + 分布式小批量验证 |
| 分片语法识别不全 | missing_info 频繁出现 | 接受降级,依赖 DBA 手动补充 |