Django批量更新JSONField字段键名 320万条记录提速方案咨询
批量更新JSON字段键名优化方案
注:以下方案基于PostgreSQL数据库,若使用MySQL,可替换为JSON_REMOVE、JSON_SET等对应JSON操作函数实现相同逻辑。
方案1:Django ORM 原生实现(无数据往返Python开销)
该方案完全在数据库层面执行操作,避免了循环拉取数据、序列化、单条save的高额IO开销,320万条数据通常几分钟即可执行完成,性能是Python循环方案的百倍以上。
from django.db.models import F, Func, Value MyModel.objects.filter( # 只处理存在旧键的记录 stats__has_key="my_old_key" ).exclude( # 跳过已经存在新键的记录 stats__has_key="my_new_key" ).update( stats=Func( # 第一步:新增my_new_key,值复用原my_old_key的值 Func( F("stats"), Value("my_new_key"), F("stats__my_old_key"), function="jsonb_set" ), # 第二步:删除旧键my_old_key Value("my_old_key"), function="-" ) )
方案2:直接执行原生SQL
如果需要更灵活的控制,可直接执行以下SQL,既可以在数据库客户端直接运行,也可以通过Django的cursor调用:
UPDATE myapp_mymodel SET stats = (stats - 'my_old_key') || jsonb_build_object('my_new_key', stats->'my_old_key') WHERE stats ? 'my_old_key' AND NOT stats ? 'my_new_key';
对应Django调用代码:
from django.db import connection with connection.cursor() as cursor: cursor.execute(""" UPDATE myapp_mymodel SET stats = (stats - 'my_old_key') || jsonb_build_object('my_new_key', stats->'my_old_key') WHERE stats ? 'my_old_key' AND NOT stats ? 'my_new_key'; """)
额外优化建议
- 执行前可先给stats字段建GIN索引,大幅提升过滤速度,建索引语句:
CREATE INDEX idx_mymodel_stats_gin ON myapp_mymodel USING GIN(stats); - 如果是线上业务怕一次性锁表影响服务,可在WHERE条件中增加主键范围分段执行,比如每次处理10万条,分批次提交避免长事务。
- 全量执行前建议先筛选少量数据验证逻辑正确性,避免误操作。
内容的提问来源于stack exchange,提问作者Revinder
相关产品推荐
相关产品推荐

