从 MySQL 迁到 KingbaseES 之后:用 MCP 排查一条慢 SQL

系统切到 KingbaseES 已经有一段时间,订单报表平时也一直正常。后来,线上按日查询已支付订单的接口开始变慢,排查最后落到了对应的 SQL 上。

这条查询不长:返回订单号、客户编码、下单时间和金额,再按时间排序。没有表关联,也没有子查询。顺着 WHERE 条件往下看,order_timeto_char 包了一层:

sql 复制代码
AND to_char(order_time, 'YYYY-MM-DD') = '2026-07-15'

这段条件是从 MySQL 迁移后保留下来的。它不报错,查询结果也正确,因此一直没有引起注意。但对 KES 来说,这不是一个可直接用于索引访问的时间范围:扫描到的每行数据都要先执行一次 to_char,然后再和日期字符串比较。

接下来要确认的是,函数条件在执行计划中落在哪个节点,现有索引为什么没有被使用,以及改成时间范围后访问路径会怎么变。这次把 Kingbase-MCP 接入 Codex,由它在只读权限下读取表结构和静态执行计划;实际运行 SQL、创建索引、收集统计信息和核对结果,仍在 DBA 的 ksql 会话中完成。

先把线上问题压缩成可复现样本

线上慢 SQL 不适合直接拿来反复试验。这里按原查询的字段、状态分布和日期条件建立了脱敏订单表 app_schema.t_mcp_order_query,数据库版本为 KingbaseES V009R001C010 / V9R1C10

验证表共有 20 万行,时间范围从 2026-06-01 00:00:002026-08-04 19:32:52。目标日期 2026-07-15 有 2469 条 PAID 订单。执行计划前后如果命中的不是同一批数据,耗时差异就没有比较价值。

MCP 服务与 KES 位于同一台服务器,只监听 http://127.0.0.1:8000/mcp,访问模式为 restricted。Mac 上的 Codex 通过 SSH 本地转发访问 http://127.0.0.1:18000/mcp,数据库端口不需要向客户端开放。

bash 复制代码
ssh -N -L 18000:127.0.0.1:8000 root@todoitbo

这套连接方式只解决"如何安全到达 MCP",并不放宽数据库权限。MCP 使用独立账号 mcp_readonly,只读取明确授权的对象。

第一次调用没有读到表

新建验证表后,Codex 第一次通过 MCP 查询 information_schema.columnsinformation_schema.tables,返回的都是空数组。站在管理员账号的视角,表明明已经存在;站在 mcp_readonly 的视角,它却是不可见的。

这不是 MCP 连接失败,而是对象授权生效了。元数据查询同样受当前数据库身份约束,账号没有访问权限时,工具不能越过 KES 去读取对象结构。随后由管理员只补充验证表所需的最小权限:

sql 复制代码
GRANT USAGE ON SCHEMA app_schema TO mcp_readonly;
GRANT SELECT ON app_schema.t_mcp_order_query TO mcp_readonly;

没有授予建表、修改数据或创建索引权限。授权完成后不需要重启 MCP,后续连接按 KES 权限重新检查,新表的结构和静态执行计划已经可以正常读取。

这个小插曲反而把安全边界验证得很清楚:MCP 能看到什么,不由自然语言请求决定,而由数据库账号的实际权限决定。

原查询为什么走了顺序扫描

先在 ksql 中执行实际计划,保留运行时间、缓冲区命中和实际行数:

sql 复制代码
EXPLAIN (ANALYZE, BUFFERS)
SELECT order_id, customer_code, order_time, order_amount
FROM app_schema.t_mcp_order_query
WHERE order_status = 'PAID'
  AND to_char(order_time, 'YYYY-MM-DD') = '2026-07-15'
ORDER BY order_time, order_id;

计划中的核心访问路径是 Parallel Seq Scan。两个并行执行单元分别扫描并过滤数据,随后执行 Sort,最后由 Gather Merge 合并有序结果。实际返回 2469 行,共命中 1816 个共享缓冲块,执行时间为 190.812 ms

20 万行并不是一个很大的数据量,但这个计划已经暴露了两个问题。首先,to_char(order_time, ...) 把时间列包在函数中,条件无法直接形成 order_time 的起止范围;其次,当时现有的 idx_mcp_order_status 只包含 order_status,而 PAID 约占数据的八成,只靠状态字段筛选的选择性很低。优化器最终认为并行顺序扫描比读取大量索引项再回表更合适。

实际行数和估算行数也有明显偏差。计划估算每个并行分支返回 471 行,最终汇总得到 2469 行。日期被包装在函数表达式中后,优化器很难直接利用时间列统计信息估算该自然日的分布。估算偏差不一定单独造成慢 SQL,但会影响扫描方式、并行和排序等后续选择。

MCP把执行计划翻译成可讨论的证据

同一条 SQL 原样交给 Kingbase-MCP 的 explain_query,参数设置为 analyze=false。这里让 MCP 读取静态计划,不由它实际跑完查询:

text 复制代码
调用 kingbase-mcp 的 explain_query,设置 analyze=false,
返回扫描节点、过滤条件、估算行数、排序节点和总估算成本。

MCP 返回了 Seq ScanSortGather Merge,总估算成本上限为 4903.91。过滤条件中能够直接看到 to_char(order_time, 'YYYY-MM-DD'),估算行数为 471。Codex 随后把各节点整理成表格,函数条件落在 Filter、现有状态索引没有被采用,这两个关键点可以直接对照计划节点确认。

静态计划和实际计划承担的任务不同。analyze=false 只调用优化器生成计划,不包含真实执行时间和实际行数;EXPLAIN ANALYZE 会真正执行查询。本次把前者交给 restricted 模式下的 MCP,用于快速读取结构、过滤条件和成本,把后者留在 DBA 控制的 ksql 会话中。这样既能利用 Codex 对计划的归纳能力,也不会让一次自然语言分析请求不受控制地执行高成本 SQL。

此时 MCP 没有替数据库"做决定"。它把 SQL、表结构和优化器计划放到同一个上下文中,能快速回答几个具体问题:扫描发生在哪个节点,函数条件落在 Filter 还是 Index Cond,预估行数是否异常,排序有没有被消除。DBA 仍然需要结合数据分布、业务峰值和变更风险判断下一步。

修改条件,同时补上匹配访问路径的索引

日期筛选改成左闭右开的时间范围:

sql 复制代码
order_time >= TIMESTAMP '2026-07-15 00:00:00'
AND order_time < TIMESTAMP '2026-07-16 00:00:00'

不使用 BETWEEN '2026-07-15 00:00:00' AND '2026-07-15 23:59:59',是因为时间精度可能包含小数秒。左闭右开范围既覆盖当天全部记录,也不会误带第二天零点的数据。

查询同时包含 order_status 等值条件和 order_time 范围条件,因此在验证环境中建立 (order_status, order_time) 联合索引,并重新收集表统计信息:

sql 复制代码
CREATE INDEX idx_mcp_order_status_time
  ON app_schema.t_mcp_order_query (order_status, order_time);

ANALYZE app_schema.t_mcp_order_query;

索引创建和 ANALYZE 都由管理员在 MCP 之外执行。重新运行实际计划后,访问路径变为 Bitmap Index ScanBitmap Heap Scan,状态条件和两个时间边界全部进入 Index Cond。索引先定位 2469 条候选记录,堆扫描只访问 28 个精确数据块;最终执行时间为 3.920 ms

排序节点仍然存在。位图扫描不会保留 B-tree 的索引顺序,而结果还要求按 order_time, order_id 排序,所以优化器使用了内存中的 quicksort,占用 289kB。这个排序只处理 2469 行,实际耗时很短,没有必要为了消掉它立刻继续扩大索引。若盲目把返回列和排序列都塞进索引,会增加存储、写放大和后续维护成本。

(order_status, order_time) 也不是可以套用到所有订单查询的固定答案。它适合当前"状态等值、时间范围"的访问方式。生产实施前还要检查同表其他高频 SQL、索引重复度、磁盘空间、创建索引时的锁影响和回滚方案。一次样本计划只能证明这条查询在当前数据分布下选中了该索引。

性能变快以后,先核对业务结果

SQL 优化最危险的情况不是"没有变快",而是"很快地返回了错误结果"。因此没有直接拿两次耗时宣布结束,而是分别对原始条件和时间范围条件计算行数、金额合计、首条时间和末条时间。

两条 SQL 都返回 2469 行,金额合计均为 3637216.59,首条订单时间为 2026-07-15 00:00:16,末条为 2026-07-15 23:59:28。四项结果完全一致,说明改写没有改变当天已支付订单的业务范围。

只比较 count(*) 仍然有漏洞:行数相同并不代表一定是同一批记录。金额合计和首尾时间提供了额外校验。正式上线时还可以用主键集合差集做更严格的验证,确认两条 SQL 不存在"数量相同、记录不同"的情况。

再让 MCP 复核一次,而不是凭耗时下结论

索引创建完成后,Codex再次通过 explain_query(analyze=false) 读取改写后 SQL 的静态计划,并调用对象详情核对实际索引。计划从顺序扫描变为 Bitmap Index Scan + Bitmap Heap Scan,三个筛选条件进入索引访问范围,Gather Merge 消失,Sort 保留。

静态计划的总成本上限从 4903.91 降至 2114.04,下降约 56.9%。估算返回行数则从 471 变为 2356,更接近实际的 2469 行。这不是性能变差,而是时间范围条件让优化器能够使用 order_time 的列统计信息,基数估算更接近真实分布。

成本值是优化器内部用于比较候选计划的相对量,不等于毫秒。实际执行时间应以 ksqlEXPLAIN ANALYZE 为准:这组 20 万行脱敏数据中,单次执行从 190.812 ms 降到 3.920 ms。这个数字可以说明本次修改有效,但不能直接外推到生产环境;缓存状态、并发、硬件、数据倾斜和参数配置都会影响绝对耗时。

MCP 的价值在复核阶段表现得更明显。它没有只回答"已经命中索引",而是继续检查索引名称、列顺序、过滤条件所在节点、估算行数和遗留排序。对运维人员而言,这比一句笼统的"建议建立索引"更有用,因为每个判断都能回到 KES 返回的计划节点。

AI 可以加快排查,但不能继承 DBA 权限

传统慢 SQL 排查经常卡在信息传递上:开发人员提供 SQL,DBA 查询表结构和索引,双方再围绕计划节点来回确认。接入 MCP 后,Codex 可以在授权范围内直接读取 KES 元数据和静态计划,把原始输出整理成可核对的结论。对于条件改写、估算偏差和索引列顺序这类问题,定位速度确实会更快。

但连接更方便,也意味着权限设计要更保守。本次环境保留了几条明确限制:

  • MCP 使用 restricted 模式,HTTP 服务只监听服务器回环地址;
  • 数据库使用独立的 mcp_readonly,只授予目标 schema 的 USAGE 和指定对象的 SELECT
  • MCP 只读取静态计划,实际运行计划由 DBA 在受控终端执行;
  • CREATE INDEXANALYZE、上线和回滚均由人工完成;
  • 生产 SQL 在执行前仍需检查锁、资源消耗和业务窗口。

即使当前工具列表里没有 UPDATEDELETE 或 DDL,也不能给 MCP 配置高权限业务账号。客户端能力会变化,工具会升级,SQL 校验也可能增加新的执行路径。KES 账号只保留必要的 SELECT 后,即使上层出现误调用,写操作仍会在数据库权限检查处被拒绝。图中未授权对象无法被发现,就是这道边界的直接结果。

最终落地的改动并不复杂:日期函数改成时间范围,增加一条与过滤条件匹配的联合索引。MCP 读取对象和计划,Codex 归纳扫描路径与过滤条件,DBA 选择并实施变更,实际计划和结果校验负责收口。

数据库运维会越来越多地使用 AI 辅助,但速度不应来自跳过验证。只读连接、静态分析、人工变更和结果复核同时保留,MCP 才适合进入长期运维流程,而不是停留在一次看起来很聪明的演示里。

相关推荐
会编程的土豆1 小时前
GORM 常见操作从入门到能写 DAO
数据库·gorm
A15362552 小时前
2026 电商物流系统推荐:多渠道仓配履约数字化选型指南
大数据·数据库·人工智能
tianyu2342 小时前
MySQL字符串处理函数完全指南:从入门到实战
mysql·字符串·函数·截取
天空之外1362 小时前
Docker安装MySQL 8.4 LTS、Docker安装Redis 7.4.x、Docker安装MinIO
java·mysql·docker
ClouGence3 小时前
CloudDM:开源免费!一站式数据库访问、SQL审核、权限与脱敏管控平台
数据库·sql·开源
(轻舟已过万重山)3 小时前
第39章 评估迭代:上线只是开始,迭代才是关键
大数据·数据库·人工智能
oradh3 小时前
Oracle参数文件(PFILE与SPFILE)维护操作总结
数据库·oracle·oracle参数·spfile·pfile
l1t4 小时前
kryonix提交的DuckDB 统一并优化标量执行器基础设施 - #24564 PR
开发语言·数据库·数据仓库·sql
鸽芷咕4 小时前
【金仓数据库征文】从 Oracle 到金仓:一次零误差的数据库国产化迁移实录
数据库·oracle
0566465 小时前
RAG 向量检索:从“查字“到“查意“
数据库·人工智能·学习·oracle