
2026 年 2 月,AI Agent 平台 Moltbook 上线仅 120 小时即全面瘫痪。安全机构 Wiz 的事故报告只是点燃了舆论关注度的引线——真正的性能灾难,早在高并发 Agent 涌入数据库的那一刻就已经埋下。这场事故暴露的,是 PostgreSQL RLS(Row Level Security)在 AI Agent 平台 + Supabase 架构下被严重低估的高并发陷阱。

一、事故现场:监控数据还原性能雪崩全过程
事故前的监控截图与指标曲线清晰地记录了崩溃是如何一步步发生的:
- 数据库 CPU 从 30% 平稳状态飙升至 100%,并持续 40 分钟无法回落
- API 响应 P99 Latency 从 120ms 恶化至 4.5s,放大 37 倍
- Supabase 连接池耗尽,新请求直接被拒绝,前端表现为大面积超时
表面看像是流量过大引发的简单容量问题,但工程师溯源后发现:根因是一个看起来”更安全”的决策——开启 PostgreSQL RLS 之后,查询性能反而雪崩。
二、背景:RLS 是什么,为什么 Moltbook 选择它
RLS(Row Level Security,行级安全策略)是 PostgreSQL 9.5 引入的核心安全特性。它允许数据库在 SQL 执行层而非应用层强制执行访问控制,理论上能做到:
| 特性 | 传统应用层权限控制 | 数据库 RLS 层权限控制 |
|---|---|---|
| 数据隔离 | 依赖应用代码 WHERE 过滤 | 数据库内核强制执行 |
| 越权风险 | 应用漏洞即可导致数据泄露 | SQL 层面无法绕过 |
| 多租户管理成本 | 每个应用需单独实现 | 策略统一管理 |
| 审计追溯 | 依赖应用日志 | 数据库日志完整可查 |
| 性能开销 | 通常较低 | 随策略数量与并发量显著增长 |
Moltbook 作为 AI Agent 平台,业务模型涉及大量用户的私密对话、Agent 配置、个人偏好数据。在 agents、conversations、messages 这三层嵌套表结构中,每一层都包含多租户数据。选择开启 RLS 本是合理且正确的安全决策——然而,这个决策在超大规模并发场景下,暴露出设计时未充分考虑的性能隐患。
三、深层原因:RLS 策略评估的四重高并发陷阱
1. 策略评估计算成本被严重低估
RLS 的工作原理决定了它的开销:每一条 SQL 在执行前,都要经过所有已启用的 Policy 评估。Moltbook 事件中,对外宣传的 Agent 数量被推至 150 万量级,真实脚本并发也能轻松达到 10 万级别。当 10 万个 Agent 同时通过脚本并发写入时,PostgreSQL 的 RLS Policy Evaluation 产生了近似笛卡尔积级别的计算压力。
-- 实际执行时,数据库内部等价于:
SELECT * FROM agents
WHERE
owner_id = auth.uid() -- Policy 1: 所有权检查
AND agent_status IN (
SELECT status FROM agent_status_policies WHERE ...
) -- Policy 2: 状态过滤
AND EXISTS (
SELECT 1 FROM users
WHERE users.id = agents.owner_id
AND users.banned = false
); -- Policy 3: 用户状态联查
Moltbook 的 schema 设计中,agents 表挂了 7 条 RLS 策略,其中 3 条涉及跨表 JOIN。在 10k+ 并发写入场景下,每条 SQL 的策略评估耗时从基线的 0.5ms 膨胀至接近 200ms,呈数百倍级别恶化。
2. 策略数量膨胀引发的隐性放大
很多人以为只要 SQL 写得快,RLS 就不慢。但实际上,RLS 的评估成本不是线性叠加,而是随策略数量与策略复杂度乘法级放大。以 7 条策略、3 条带 JOIN 为例,在最坏情况下数据库需要为每一条进入 agents 的 SQL 同时评估 7 次条件判断 + 3 次子查询或关联查询。当基础表行数超过千万级、单次 Policy 评估本身就要扫描数万行时,整体 P99 延迟会被迅速推高到秒级。
3. 跨表 JOIN 评估导致的执行计划膨胀
带 EXISTS、子查询或 JOIN 的 RLS 策略,最大的隐患在于:优化器无法把这些策略条件视作普通谓词下推。在某些情况下,PostgreSQL 会选择先做全表扫描再过滤,而不是利用索引;当多个策略并存时,规划器甚至会生成多个相互冲突的执行计划,导致 CPU 长时间处于规划与重规划状态。Moltbook 事故中观测到的”CPU 100% 持续 40 分钟回落不了”,很大一部分时间是被规划器与策略评估共同吃掉的。
4. 连接池争抢放大故障半径
RLS 评估变慢直接导致单条 SQL 持有连接的时间变长。Supabase 默认的连接池在事务模式下容量是有限的,当所有连接都被慢查询占住时,新请求拿不到连接就被拒。这正是 Moltbook 出现”连接池耗尽”的原因——它不是流量问题,而是被 RLS 拖慢的慢查询把连接池堵死了。
四、解决方案与优化建议:给 PostgreSQL + RLS 用户的实操清单
对正在使用 RLS 的 PostgreSQL 团队(尤其是 Supabase 用户),可参考以下递进式优化策略:
策略层:减少单表 RLS 策略数量
- 将可以合并的多个策略合并为一条
USING (...)表达式,避免单表超过 4–5 条 Policy - 不在 RLS 策略中写
auth.uid() = (...),再 JOIN 一张用户表,如果能在 users 表上保证id = auth.uid()可直接索引,应改写为对索引列的等价判断 - 对极少使用的安全规则改用应用层校验,避免冷策略持续被评估
下沉层:把跨表 JOIN 策略沉到应用层
- 当策略涉及 EXISTS 子查询或跨表时,评估一次的成本远高于在应用层加一次 JOIN
- 用物化视图或缓存表把”用户状态/账户封禁”等慢变字段提前同步到
agents表的冗余列,RLS 改为纯本地列判断 - 谨慎使用
security_barrier = true,它在提升安全性的同时也会禁用谓词下推,仅在确实存在侧信道攻击风险时再开启
索引层:用 partial index 与函数索引优化
- 对 RLS 策略中的常用谓词组合建立 partial index,例如
CREATE INDEX ... ON agents(owner_id) WHERE deleted_at IS NULL - 如果策略里有
auth.uid()这种稳定函数,可考虑将其结果物化进会话变量或表列,配合部分索引提升命中
架构层:读写分离分摊压力
- 把热写入路径与冷查询路径拆到不同 Supabase Project 或不同 PostgreSQL 实例
- 利用 Supabase 的 Read Replica,把读流量分流到只读节点,避免读路径的 RLS 评估抢占主库 CPU
- 在 Supabase 池化配置上,将
pool_mode切到transaction并合理调高max_client_conn,配合应用层限流,避免雪崩
观测层:必加的 RLS 监控项
- 启用
pg_stat_statements,记录被 RLS 拖慢的 Top SQL - 监控
pg_locks与pg_stat_activity中长事务,及时 kill 持锁过久的 RLS 查询 - 在 Supabase 控制台打开
log_min_duration_statement,对超过 1s 的查询全量采样
五、事故后续与行业影响:2026 年的修复进展与社区回应
截至 2026 年 8 月,Moltbook 事故发生已过去约半年,行业内出现了几个值得关注的修复与讨论动向:
- Supabase 侧:官方在 2026 年 Q2 发布的更新中强化了连接池在事务模式下的背压能力,并新增了”策略数量超阈值告警”,帮助团队提前发现 RLS 复杂度过高的表。多个第三方报告显示,更新后类似规模的并发写入场景下,连接耗尽的发生概率明显下降。
- PostgreSQL 社区侧:在 PG 全球开发者邮件列表与年度大会上,出现了若干篇关于”RLS 在多租户 SaaS 中的真实开销”的实测分享。主流建议趋于一致:RLS 适合做”最后一道兜底防线”,而非唯一的访问控制机制;复杂的业务级访问规则仍建议交由应用层处理。
- Moltbook 侧:根据公开的事故复盘文章与社区讨论,Moltbook 在事故后对
agents、conversations、messages三张核心表进行了策略精简与冗余列改造,将单表 RLS 策略条数从 7 条压缩到 3 条以内,并启用了读副本分流。短期内仍偶发的小规模延迟问题,已通过连接池扩容与限流策略被压住。 - 更广泛的影响:2026 年上半年陆续有数家 AI Agent 平台在技术博客中复盘自家踩过的 RLS 坑,使得”RLS 适合做兜底而非主力”几乎成为业内共识。PostgreSQL 生态内围绕”如何在不放弃安全性的前提下压低 RLS 评估开销”的工具与中间件也明显增多。
六、给 AI Agent 平台架构师的避坑清单
- 不要把 RLS 当唯一的访问控制层。它适合做安全兜底,复杂的业务权限仍应走应用层。
- 单表 Policy 总数控制在 4 条以内,跨表策略不超过 1 条。
- 跨表 JOIN 的 RLS 策略是高危项,要么下沉到应用层,要么用冗余列 + 本地判断替代。
- 写并发高的表尤其需要谨慎评估 RLS,一条慢评估拖垮整池连接是真实发生过的。
- 把 Supabase 连接池容量视为安全冗余的一部分,但不要把它当作性能银弹。
- 新表上线前做一次高并发回放,重点看 RLS 评估有没有激增。
- 持续关注 pg_stat_statements 中被 RLS 拖慢的 Top SQL,定期回顾能不能砍。
七、常见问题(FAQ)
Q:RLS 是不是不能用了?
A:不是不能用,而是不能滥用。RLS 在中小规模、多租户需要严格数据隔离的场景下仍是首选,但在大规模高并发场景下需要搭配上述优化手段使用。
Q:不开 RLS,有没有等价的替代方案?
A:可以在应用层通过统一的 ORM 中间件或独立的 BFF 服务集中处理权限过滤,效果接近但绕开了数据库层的开销,代价是要自己保证所有写入路径都经过中间件。
Q:怎么判断现在的 RLS 策略是不是太多?
A:如果单张表 RLS 策略超过 5 条,或者存在跨表 EXISTS/JOIN 的策略,又或者 pg_stat_statements 中命中该表的 SQL 平均耗时显著高于同结构无 RLS 表,那就该精简了。
Q:Supabase 项目默认就开 RLS 吗?我新表创建需要注意什么?
A:Supabase 在新建表时默认会启用 RLS,但不会自动加任何策略,等于”开了门但没发钥匙”,访问会被默认拒绝。建议新表创建时同步敲定策略,并按上文清单做一次复杂度评估。
Q:有没有办法在保留 RLS 安全性的前提下基本消除性能影响?
A:在 2026 年的工程实践里,比较成熟的组合是:精简策略到 3 条以内 + 冗余列替代跨表 JOIN + 读写分离 + 部分索引命中。这套组合下,RLS 对 P99 延迟的影响通常可以控制在毫秒级。