为什么AI写数据库代码容易“翻车”
AI编码智能体如今已经能写出看起来很像那么回事的SQL语句,但数据库不是玩具,而是一台精密运转几十年的工业引擎。
Postgres自1996年诞生以来,积累了海量特性、边界情况和性能细节,这些知识藏在文档深处、社区经验里,甚至只存在于老DBA的脑回路中。一个看似正确的查询,可能在生产环境引发全表扫描;一个随手加的索引,可能拖慢所有写入操作;
更危险的是,AI可能完全忽略行级安全(Row Level Security, RLS),导致用户数据被随意读取。这些问题不是语法错误,而是系统理解偏差——AI知道“怎么写”,但不知道“为什么不能这么写”。
于是,Supabase团队把多年支持数十万项目的实战经验,打包成一套机器可读的“驾驶手册”,直接喂给AI。
这套技能包到底教了些什么
这套Postgres最佳实践技能包按影响程度分为8个类别,优先级从高到低依次是:查询性能(Critical)、连接管理(Critical)、安全与RLS(Critical)、模式设计(High)、并发与锁(Medium-High)、数据访问模式(Medium)、监控诊断(Low-Medium)和高级特性(Low)。
每条规则都采用统一结构:先用一句话点明核心价值,再对比“错误示范”与“正确做法”,最后附上权威参考链接。比如在RLS这一关键安全机制上,规则明确指出:仅靠应用层过滤用户数据是极其危险的,因为一旦代码出错或被绕过,整张表的数据就裸奔了。正确的做法是在数据库层面启用RLS,并通过策略强制隔离。
行级安全:数据库自带的“防盗门”
行级安全(RLS)是Postgres内置的一项强大功能,它能在数据库内部自动为每个查询加上“WHERE”条件,确保用户只能看到自己有权访问的数据。
举个例子,假设有一张orders表,里面存着所有用户的订单。如果只靠应用代码写“WHERE user_id = 当前用户ID”,那只要有人绕过应用直接查表,或者代码漏写了这个条件,所有订单就暴露了。而启用RLS后,无论谁来查,数据库都会自动加上这个过滤条件。
具体操作分三步:
首先用“ALTER TABLE orders ENABLE ROW LEVEL SECURITY”开启RLS;
然后创建策略,比如“CREATE POLICY orders_user_policy ON orders FOR ALL USING (user_id = auth.uid())”;
最后,为了防止表所有者(如管理员)意外绕过RLS,还可以加上“ALTER TABLE orders FORCE ROW LEVEL SECURITY”。
这样一来,即使拥有最高权限的用户,也必须遵守这条规则。
Supabase如何让RLS更简单
在Supabase生态中,RLS与身份认证深度集成。当用户通过Supabase Auth登录后,其唯一用户ID(UUID)会自动注入到数据库会话中,通过“auth.uid()”函数即可获取。
这意味着策略可以写得非常简洁:“USING (auth.uid() = user_id)”。
但要注意,如果请求未认证(比如用anon密钥访问),“auth.uid()”会返回null,而“null = user_id”在SQL中永远为假,导致查询结果为空。
为了避免混淆,建议显式检查认证状态:“USING (auth.uid() IS NOT NULL AND auth.uid() = user_id)”。
此外,Supabase将请求映射到两个Postgres角色:“anon”代表未登录用户,“authenticated”代表已登录用户。策略可以通过“TO”子句指定适用角色,比如“TO authenticated, anon”表示所有人都能读,而“TO authenticated”则限制仅登录用户可见。
AI如何“读懂”这些规则
这套技能包采用“Agent Skills”格式,这是一种由Anthropic推动的开放标准,本质上是一组结构化的指令和示例,专为AI代理设计。当AI在写SQL时,如果遇到不确定的情况,它会主动查阅相关技能文件,就像程序员查文档一样。
例如,当AI准备为一张新表生成查询语句时,它会发现“安全与RLS”类别下的规则,于是自动建议先启用RLS并创建策略。这种机制比依赖模型训练数据更可靠,因为训练数据可能包含过时或错误的做法,而技能包提供的是经过验证的最佳实践。
目前,该格式已被Vercel、Cloudflare等公司广泛采用,用于向AI传递React、Workers、D1等复杂系统的使用规范。
技能包与MCP服务器:方向盘配驾校
Supabase还提供MCP(Model Control Plane)服务器,允许AI代理通过自然语言直接操作数据库——建表、查数据、改配置都不在话下。
但光有操作能力还不够,就像给新手一把车钥匙却不教交通规则,迟早出事。
MCP服务器是“方向盘”,让AI能控制数据库;而最佳实践技能包则是“驾校课程”,教会AI如何安全驾驶。两者结合,AI不仅能执行“给我加个索引”的指令,还会主动提醒:“这个索引会导致写入变慢,建议先评估数据量”;或者在生成查询前问:“是否需要为这张表启用RLS?否则可能存在数据泄露风险”。
这种“能做”与“该做”的分离,正是智能体自说自话迈向成熟的关键一步。
规则背后的血泪教训
这些规则并非凭空想象,而是来自Supabase支持团队每天处理的真实案例。
比如,无数项目忘记在外键列上建索引,导致关联查询慢如蜗牛;
又比如,开发者在迁移脚本中执行“ALTER TABLE ADD COLUMN”却未考虑锁表时间,结果线上服务卡死;
还有ORM框架隐藏了全表扫描,直到数据量暴涨才暴露性能瓶颈。
更常见的是RLS被遗漏——开发时一切正常,上线后才发现匿名用户也能看到别人的数据。
把这些高频“翻车点”提炼成规则,等于给AI装上了“事故预警雷达”,让它在生成代码前就规避掉这些坑。
如何安装和使用这套技能
这套技能包已开源在GitHub(github.com/supabase/agent-skills),任何人都能使用。
如果用的是Vercel的AI工具链,只需运行“npx skills add supabase/agent-skills”即可一键安装。
如果是Claude Code用户,则可通过插件市场添加:“/plugin marketplace add supabase/agent-skills”,再执行“/plugin install postgres-best-practices@supabase-agent-skills”。
安装后,AI在编写Postgres相关代码时会自动引用这些规则,无需手动干预。
随着社区贡献增加,技能包还会覆盖更多边缘场景,比如分区表优化、JSONB索引策略、逻辑复制陷阱等。
开源共建:你的经验也能变成AI的常识
Supabase鼓励社区贡献新规则。只要遇到过“AI绝对想不到但实际会炸”的Postgres坑,都可以提交PR。每条规则需包含清晰标题、优先级(Critical/High/Medium/Low)、问题说明、正反代码示例。
比如,有人可能提交一条关于“避免在CTE中使用非确定性函数”的规则,因为这会导致查询结果不可预测;或者关于“使用COPY而非INSERT批量导入数据”的性能建议。这种众包模式能让AI的知识库持续进化,最终形成一个覆盖Postgres全生命周期的“集体智慧体”。
从“能跑就行”到“生产就绪”的跨越
过去,AI生成的数据库代码往往停留在“能跑就行”的阶段——语法正确、小数据量下表现尚可,但一上生产就露馅。现在,有了这套技能包,AI开始具备“生产就绪”的意识。它知道索引不是越多越好,知道事务隔离级别会影响并发,知道RLS是数据安全的最后防线。
这种转变的意义,不亚于从手工作坊进入工业化时代。开发者不再需要逐行审查AI生成的SQL,而是可以信任它已经遵循了行业最佳实践。这不仅提升效率,更从根本上降低了系统性风险。
代码示例:RLS的正确打开方式
以下是一个完整的RLS配置示例,展示了如何在Supabase中为多租户应用设置安全策略:
sql
-- 启用行级安全
alter table orders
enable row level security;
-- 创建策略:仅允许已认证用户访问自己的订单
create policy orders_user_policy on orders
for all
to authenticated
using (user_id = auth.uid());
-- 强制RLS,连表所有者也不能绕过
alter table orders
force row level security;
相比之下,错误的做法是仅依赖应用层过滤:
sql
-- 危险!一旦漏写WHERE条件,全表数据暴露
select * from orders;
有了RLS,即使应用代码出错,数据库也会自动拦截非法访问,形成双重保险。
性能优化:别让AI写出“慢查询”
除了安全,性能也是AI容易踩的雷区。比如,AI可能写出这样的查询:
sql
select * from users where name like '%john%';
如果没有在name列上建立合适的全文索引或GIN索引,这种模糊查询会触发全表扫描。技能包中的“查询性能”规则会提醒AI:避免在WHERE子句左侧使用函数或通配符,优先使用等值匹配,并确保相关列有索引。另一个常见问题是分页查询:
sql
select * from logs order by id limit 100 offset 900000;
当offset很大时,数据库仍需扫描前90万行,效率极低。正确做法是使用游标分页(基于上一页最后一条记录的id继续查询),技能包会引导AI采用这种模式。
连接管理:别让数据库“窒息”
AI还可能忽略连接池的重要性。每个数据库连接都消耗内存和CPU资源,如果应用频繁创建新连接而不复用,很快会耗尽数据库的max_connections限制,导致新请求被拒绝。
技能包中的“连接管理”规则会强调:必须使用连接池(如pgBouncer),并在客户端正确管理连接生命周期——用完及时归还,避免长时间闲置。
此外,还要设置合理的statement_timeout,防止慢查询长期占用连接。
并发与锁:别让事务“打架”
在高并发场景下,事务隔离和锁管理至关重要。AI可能写出这样的更新语句:
sql
update accounts set balance = balance - 100 where user_id = 123;
如果多个事务同时执行,可能因竞态条件导致余额计算错误。技能包会建议使用SELECT FOR UPDATE锁定行,或采用原子操作(如Postgres的UPSERT)。对于可能引发死锁的复杂事务,规则会推荐按固定顺序访问表,缩短事务持续时间,从而降低冲突概率。
监控与诊断:让问题“现形”
即使代码写得再好,生产环境仍可能出现意外。技能包中的“监控诊断”规则会引导AI在生成代码时考虑可观测性。比如,建议在关键查询中添加EXPLAIN ANALYZE注释,方便后续性能分析;或在慢查询日志中设置阈值,自动捕获异常。
AI甚至可以建议创建专门的监控视图,聚合活跃会话、锁等待、缓存命中率等指标,让运维人员一目了然。
高级特性:用对才能事半功倍
Postgres拥有大量高级功能,如窗口函数、CTE、物化视图、JSONB等。AI可能滥用这些特性,比如在CTE中嵌套复杂子查询导致性能下降,或过度使用JSONB而牺牲了关系型优势。
技能包会明确适用场景:窗口函数适合排名、累计计算;CTE用于提高可读性但不总是优化性能;JSONB适合存储半结构化数据但需谨慎索引。通过这些指导,AI能更精准地选择工具,而不是“拿着锤子看啥都是钉子”。
社区的力量:让AI越用越聪明
这套技能包的独特之处在于其开放性和可扩展性。它不依赖某个AI模型的内部训练,而是以独立文件形式存在,任何支持Agent Skills标准的工具都能使用。这意味着,全球开发者贡献的经验,都能实时转化为AI的“常识”。
今天你提交一条关于分区裁剪的规则,明天全世界的AI写分区表查询时都会受益。这种“集体学习”机制,让AI的知识库不再是静态的,而是动态演化的生态系统。