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

使用Django ORM bulk_create批量插入PostgreSQL数据逐渐变慢求助

我之前也碰到过类似PostgreSQL搭配Django bulk_create()越跑越慢的问题,给你几个实际验证有效的优化方向:

1. 调整批量插入的批次大小

你当前用的是1000条一批,但PostgreSQL对单批次的最优值并非固定——太大的批次会让数据库WAL(预写日志)压力陡增,太小又会频繁触发事务开销。建议根据单条记录的字段数量、是否包含大文本等,测试500、2000甚至5000的批次大小。我之前把批次调到2000后,插入速度稳定了不少。

2. 手动控制事务,减少事务开销

Django默认每个bulk_create会开启独立事务,循环里频繁创建/提交事务会产生额外开销。可以用transaction.atomic()将多个批次包裹,或者手动控制每个批次的事务:

from django.db import transaction
import time

entries = []
start = time.time()
for i, row in enumerate(initial_data_list):
    serializer = serializer_class(data=row, **kwargs)
    serializer.is_valid(raise_exception=True)
    entries.append(MyModel(**serializer.initial_data))
    
    if i % 2000 == 0 and entries:
        with transaction.atomic():
            MyModel.objects.bulk_create(entries)
        end = time.time()
        _logger.info('processed %d inserts... (%.2f seconds per batch)' % (i, end-start))
        start = time.time()
        entries = []  # 清空批次列表

# 处理最后一批剩余数据
if entries:
    with transaction.atomic():
        MyModel.objects.bulk_create(entries)

这样能减少事务的创建和提交次数,降低数据库锁竞争与日志写入的额外消耗。

3. 临时禁用索引与约束(离线导入场景适用)

如果你的表有大量索引、外键约束或唯一约束,每插入一批数据时数据库都要更新索引、检查约束,这会随着数据量增长越来越慢。如果是一次性初始化数据这类离线场景,可以先禁用约束,导入完成后再恢复:

from django.db import connection

# 临时禁用触发器与索引(仅适合可信数据源的离线导入)
with connection.cursor() as cursor:
    cursor.execute('ALTER TABLE myapp_mymodel DISABLE TRIGGER ALL;')
    cursor.execute('DROP INDEX IF EXISTS myapp_mymodel_some_field_idx;')

# 执行你的bulk_create插入逻辑...

# 恢复触发器与索引
with connection.cursor() as cursor:
    cursor.execute('ALTER TABLE myapp_mymodel ENABLE TRIGGER ALL;')
    cursor.execute('CREATE INDEX myapp_mymodel_some_field_idx ON myapp_mymodel(some_field);')

⚠️ 注意:此操作需确保导入数据完全合法(不会违反约束),且仅能用于离线场景,绝对不能在线上实时插入时使用。

4. 优化Serializer的验证逻辑

你现在每条数据都走serializer.is_valid(),数据量超大时,这部分CPU开销会持续累积。如果initial_data_list来自可信来源(比如已经过预处理的内部数据),可以直接跳过Serializer验证,直接构造模型实例,能节省大量时间。如果必须验证,也可以考虑实现批量验证逻辑,减少重复校验的开销。

5. 调整PostgreSQL的配置参数

数据库层面的配置也会影响插入速度,建议检查以下参数:

  • 调大work_mem和maintenance_work_mem,让数据库处理批量插入时有更多内存可用,减少磁盘IO;
  • 调整max_wal_size(PostgreSQL 9.5+)和wal_buffers,优化WAL写入策略,降低checkpoint的触发频率;
  • 若使用固态硬盘,可适当调小wal_writer_delay,降低WAL写入延迟。

调整前建议先通过pg_stat_activity和pg_stat_bgwriter监控数据库状态,定位具体瓶颈。

6. 利用bulk_create的batch_size参数(Django 2.2+)

Django 2.2及以上版本的bulk_create支持batch_size参数,内部会自动将大数据集拆分为合理批次插入,省去自己写循环的麻烦,且实现更优化:

entries = []
for row in initial_data_list:
    serializer = serializer_class(data=row, **kwargs)
    serializer.is_valid(raise_exception=True)
    entries.append(MyModel(**serializer.initial_data))

# 自动分批插入
MyModel.objects.bulk_create(entries, batch_size=2000)

不过如果数据量达到百万级以上,还是建议分批构造实例,避免内存占用过高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:20:56