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

Django3.2如何高效序列化大型QuerySet并写入JSON文件

Django 3.2 大查询集序列化导出JSON优化方案

运行环境:Django 3.2.10,Python 3.9

首先明确两种原始写法的性能问题:

  • 全量传入序列化器的写法:会一次性加载10万条用户记录到内存,同时序列化器会生成完整的10万元素列表,内存占用会随数据量线性上涨,数据量更大时会直接触发内存溢出
  • 单条循环实例化序列化器的写法:一是直接迭代QuerySet会触发默认的全量结果缓存,同样会把所有记录加载到内存;二是单条初始化序列化器、重复执行dump的序列化和IO开销极高,因此执行速度极慢

核心优化原则

  • 用iterator()分批拉取数据库记录,跳过QuerySet默认缓存,迭代完一批就释放一批内存,避免全量加载
  • 只查询序列化需要的字段,杜绝无意义的字段拉取
  • 批量处理序列化逻辑,减少重复的序列化器初始化和IO操作
  • 无复杂逻辑时优先跳过模型实例化、DRF序列化器的额外开销,直接拿字典格式数据写入

方案1:需要保留DRF序列化器逻辑的场景

如果序列化过程涉及自定义字段、嵌套关联等必须用DRF序列化器的逻辑,按如下写法实现,10万条数据内存占用可稳定在50MB以内:

import json
from django.core.serializers.json import DjangoJSONEncoder

# 每批拉取的记录数,可根据服务器内存调整,建议1000-5000区间
CHUNK_SIZE = 2000
# 用only()指定需要序列化的字段,大幅降低数据库传输和内存占用;有关联字段提前加select_related/prefetch_related避免N+1
queryset = Users.objects.all().only("id", "username", "email", "join_time")

with open("output.json", "w", encoding="utf-8") as f:
    f.write("[")
    is_first_item = True
    # 分批迭代,不触发QuerySet全量缓存
    for batch in queryset.iterator(chunk_size=CHUNK_SIZE):
        # 对当前批次批量做序列化,比单条实例化序列化器效率高3倍以上
        serialized_batch = MySerializer(batch, many=True).data
        for item in serialized_batch:
            if not is_first_item:
                f.write(",")
            json.dump(item, f, cls=DjangoJSONEncoder, ensure_ascii=False)
            is_first_item = False
    f.write("]")

方案2:纯字段导出的高性能场景

如果只是将模型字段直接导出为JSON,没有自定义序列化逻辑,直接用values()返回字典结构,跳过模型实例化和DRF序列化器的额外开销,10万条数据导出耗时可压缩到2秒内:

import json
from django.core.serializers.json import DjangoJSONEncoder

CHUNK_SIZE = 5000
# 直接指定需要导出的字段,返回字典格式,无需生成模型实例
queryset = Users.objects.values("id", "username", "email", "join_time")

with open("output.json", "w", encoding="utf-8") as f:
    f.write("[")
    is_first_item = True
    for item in queryset.iterator(chunk_size=CHUNK_SIZE):
        if not is_first_item:
            f.write(",")
        json.dump(item, f, cls=DjangoJSONEncoder, ensure_ascii=False)
        is_first_item = False
    f.write("]")

避坑提示

  • 使用iterator()时不要将迭代结果追加到全局列表,否则会完全失去分批优化的意义,内存依然会持续上涨
  • 禁止在循环迭代过程中查询关联表数据,提前用select_related(一对一、外键)、prefetch_related(多对多、反向外键)预加载关联数据,避免N+1查询问题
  • 写入文件时保持文件句柄常开,不要每条记录都执行打开/关闭文件操作,减少不必要的IO开销
  • 序列化时间、UUID等Django特殊字段类型时,必须用DjangoJSONEncoder做编码,否则会触发类型报错
  • 不要为了图方便用list(queryset)将整个查询集转为列表,会直接触发全量加载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 13:15:40