MariaDB 中文社区

AI 操作数据库的事故合集

真实发生过的"Agent 把库搞坏了"故事,每条都附"用 MariaDB 特性如何预防"

看这一页,是为了让你的 Agent 不会变成下一条故事的主角

故事来自社区公开复盘(Hacker News / X / 公司事故报告),事实部分匿名化处理。每条都给出"如果当时按这一站的建议做了,会怎样"。


事故 1:Cursor 把 production 表 TRUNCATE

起因:开发者让 Cursor "clean up the test data in users",Cursor 生成 TRUNCATE TABLE users 并在自动执行模式下跑了——连的是生产库

后果:50 万用户数据归零,靠备份恢复,停服 4 小时。

为什么会发生

  • 没有 SQL 注入护栏
  • AI 工具的"db connection"只配了一个 URL,没有"危险动作必须人工确认"
  • 用的是 root 账号

如果按 MariaDB 推荐姿势

  1. AI 走 MCP server,连接走只读账号
  2. 任何 TRUNCATE/DROP/ALTER 在 guard 层就被拦掉
  3. 即使 guard 失效,账号没有这些权限也跑不动
-- AI 专用账号
CREATE USER 'ai_agent'@'%' IDENTIFIED BY 'xxx';
GRANT SELECT, SHOW VIEW ON app.* TO 'ai_agent'@'%';
-- 没有 DROP / TRUNCATE / DELETE 权限

事故 2:Claude 跑了一个没 WHERE 的 UPDATE

起因:让 Claude "把所有 trial 用户改成 paid",它生成:

UPDATE users SET plan = 'paid';

漏了 WHERE plan = 'trial'。Claude 自己其实加了 review 步骤,但开发者按了 Y。

后果:免费用户全部"升级",扣不到钱,影响营收数据 + 客服爆炸。

预防

-- guard 层强制带 WHERE
function guard(sql) {
  const lower = sql.toLowerCase();
  if (/^\s*(update|delete)/.test(lower) && !/\bwhere\b/.test(lower)) {
    return { ok: false, reason: 'write without WHERE rejected' };
  }
}

也可以在客户端规则里写:

Never generate UPDATE/DELETE without WHERE.
If you must mutate a full table, add `-- intentional: full table` comment AND ask me to confirm.

事故 3:Agent 写出 N+1 把数据库打挂

起因:让 Agent 写"导出用户订单 CSV",它写出:

for user_id in all_users:
    orders = db.query(f"SELECT * FROM orders WHERE user_id = {user_id}")

100 万用户 = 100 万次 query,1 小时打挂从库。

预防

  1. EXPLAIN 校验在 Text-to-SQL pipeline 里——能拦掉很多但拦不掉"循环里调 query"
  2. 代码级 rules:让 AI 优先用 WHERE user_id IN (...) 或 JOIN,写出循环时强制 review
  3. 慢查询告警long_query_time = 0.5,超过就告警;ChatOps 接通后告警直接 ping 写代码的人
-- 在配置层
SET GLOBAL long_query_time = 0.5;
SET GLOBAL slow_query_log = 1;

事故 4:MCP server 没限流,Agent 暴力扫描泄露数据

起因:自建 MCP server 没限流,Agent 在 prompt 注入诱导下,连续跑了 5000 次 SELECT * FROM users LIMIT 100 OFFSET ?,把所有用户数据外传。

预防

import { RateLimiterMemory } from 'rate-limiter-flexible';
const limiter = new RateLimiterMemory({
  points: 60,           // 每分钟 60 次
  duration: 60,
  blockDuration: 600,   // 触发限流后封 10 分钟
});

server.tool('run_query', /* ... */, async (args) => {
  try { await limiter.consume(client_id); }
  catch { return errorResp('rate limited'); }
  /* ... */
});

外加 审计日志,异常行为可追溯。


事故 5:向量索引建错,RAG 检索全表扫描

起因:开发者用 MariaDB VECTOR 做 RAG,忘了加 VECTOR INDEX。库里 200 万 chunks,每次问答耗时 30s+,最终拖垮数据库。

预防

-- 建表时就把索引建好
CREATE TABLE chunks (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  embed VECTOR(1536) NOT NULL,
  VECTOR INDEX (embed) M=16 DISTANCE=cosine  -- ← 必须有
);

-- 验证查询用了索引
EXPLAIN SELECT ... ORDER BY VEC_DISTANCE_COSINE(embed, ...) LIMIT 8;
-- type 应该不是 ALL

详见 RAG 教程


事故 6:MySQL 8 → MariaDB 11 迁移后,AI 生成的 JSON 函数报错

起因:迁移到 MariaDB 11 后,AI 仍然按 MySQL 8 的语法生成 JSON_TABLE,CI 全红。

预防

在 rules 文件里显式标注差异

## Differences from MySQL 8 (LLM tends to forget)
- JSON_TABLE syntax is restricted in MariaDB vs MySQL 8
- Default utf8mb4 collation differs
- caching_sha2_password does not exist
- ...

同时 迁移指南 里列了所有要改的点。


事故 7:Agent 写 schema 时把 FK 全删了说"提高性能"

起因:让 Agent "优化这个慢查询",它给的方案之一是"删除外键约束",开发者照做。3 个月后发现有大量孤立数据。

预防

## Rules
- Never suggest removing foreign keys for performance reasons
- If FK is suspected to be the bottleneck, propose `OPTIMIZE TABLE` or index changes first
- Any DDL change needs human review with rollback plan

事故 8:测试库被当成生产库

起因:CI 跑测试连的 URL 配错,写到了 production。Agent 看到"测试失败因为没数据",于是生成 INSERT 一通灌测试数据。

预防

  • 生产连接的 URL 必须包含 prod 字符串,并在应用启动时硬校验
  • 测试环境用完全不同 host 的实例,不只是不同 DB
  • read_only=1 flag 的从库给 AI 用
-- 启动时校验
SELECT @@hostname;  -- 应用层检查包含 'staging' 或 'dev'

总结:让你的 Agent 安全的 7 条最低要求

#要求落实手段
1AI 只读数据库账号 + 应用层 guard 双保险
2任何写操作必须人工确认MCP requires_approval: true 或 review step
3行数上限MCP guard 强制 LIMIT
4限流rate-limiter,每客户端每分钟 60 次
5审计日志独立审计库,独立账号
6危险语句白名单guard 层 deny DROP/TRUNCATE/ALTER/GRANT
7EXPLAIN 校验生成后自动跑 EXPLAIN,全表扫描拒绝

按这一套做下来,绝大多数事故都能避免。

想分享你的事故?

如果你有公开过的 AI + DB 事故复盘,欢迎 PR 贡献到这一页。匿名也行,让别人少踩坑。

延伸

本页目录