You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django INSERT查询性能异常及PostgreSQL autovacuum频繁运行原因咨询

问题分析与解答

背景与原实现

原本使用Django ORM执行插入操作,UserEvent表在(user, name)字段上有唯一索引,代码如下:

try:
    UserEvent.objects.create(user=user, name=name)
except IntegrityError:
    pass

通过APM监控发现该操作耗时远超预期,12小时内总耗时达到10分钟。

优化后的实现

改用PostgreSQL的ON CONFLICT DO NOTHING语法直接执行SQL:

with connection.cursor() as cursor:
    cursor.execute(
        f"""
INSERT INTO user_event (user_id, name)
VALUES (%s, %s) ON CONFLICT (user_id, name) DO NOTHING;
""",
        [user.id, name],
    )

优化后问题解决,12小时内总耗时降至5秒。

额外观察

  • 修复前,2周内PostgreSQL的autovacuum进程对该表运行超1000次;修复后仅运行数次。
  • 该INSERT操作每秒执行约20次。

问题解答

  1. 原查询耗时过高的原因:
    原实现是先尝试插入,遇到唯一键冲突时抛出IntegrityError再捕获忽略。每次冲突都会让PostgreSQL经历完整的“尝试插入(加锁、检查约束)→ 发现冲突→ 回滚事务→ 抛出异常”流程。结合每秒20次的操作量,若冲突比例较高,大量请求都会重复这个低效的“尝试-失败-回滚”过程,叠加起来导致总耗时飙升。另外Django ORM的异常捕获也会带来少量Python层面的额外开销,但核心还是数据库端的重复约束检查和事务回滚成本。

  2. autovacuum频繁运行的原因:
    PostgreSQL的autovacuum会在表产生大量死元组时频繁启动清理。原实现中,每次插入失败的回滚操作,虽然最终没有写入有效数据,但数据库已经修改了数据页,回滚后这些被标记为删除的临时数据就成了死元组。每秒20次的操作带来大量冲突,进而生成海量死元组,持续触发autovacuum启动清理,所以才会出现2周内运行超1000次的情况。改用ON CONFLICT DO NOTHING后,数据库会直接跳过冲突的插入请求,不会生成死元组,autovacuum的清理需求自然大幅降低。

内容的提问来源于stack exchange,提问作者Johnny Metz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 22:06:01