百万行数据批量插入SQL数据库失败,求高效入库解决方案
高效导入百万级CSV到Django SQL数据库的解决方案
嘿,这个坑我踩过好多次——用循环调用save()处理百万行数据,不仅速度慢到离谱,超大的事务还很容易触发数据库超时或者内存溢出问题。咱们来一步步优化:
问题根源
你原来的代码里,每循环一次就创建一个模型对象并调用save(),这意味着每一行数据都会触发一次数据库INSERT请求,100万行就是100万次请求,加上@transaction.atomic把所有操作包在一个超大事务里,数据库根本扛不住这么大的事务体量。
方案1:用Django ORM的bulk_create分批插入
这是最贴近你现有代码的优化方案,既保留ORM的便捷性,又能大幅提升性能:
from django.db import transaction import csv def insert(request): csv_filepathname = "your_file_path.csv" batch_size = 1000 # 可根据服务器内存调整,建议1000-5000之间 nela_batch = [] with open(csv_filepathname, 'r', encoding='utf-8') as csv_file: data_reader = csv.reader(csv_file, delimiter=',', quotechar='"') # 如果CSV有表头,先跳过第一行 next(data_reader, None) for row in data_reader: # 直接用模型构造器创建对象(不调用save) nela_obj = Nela( name=row[0], last=row[1], almost=row[2], number=row[3] ) nela_batch.append(nela_obj) # 达到批次大小就批量插入 if len(nela_batch) >= batch_size: with transaction.atomic(): Nela.objects.bulk_create(nela_batch) nela_batch = [] # 处理最后一批不足batch_size的数据 if nela_batch: with transaction.atomic(): Nela.objects.bulk_create(nela_batch)
为什么这个方案有效?
bulk_create会把一批对象打包成单次INSERT请求(或者少量请求,取决于数据库),把100万次请求降到几百次,性能提升几十倍。- 分批+小事务:避免超大事务占用过多数据库资源,降低超时和锁表的概率,同时控制内存占用(不会一次性把100万个对象塞进内存)。
方案2:用数据库原生导入命令(终极性能方案)
如果追求极致速度,直接用数据库的原生批量导入命令是最优解,速度比ORM快10-100倍,因为这是数据库层面的原生优化,跳过了ORM的中间层。
PostgreSQL:COPY命令
from django.db import connection def insert(request): csv_filepathname = "your_file_path.csv" with connection.cursor() as cursor: # 替换成你的实际表名和列名,有表头就加HEADER参数 cursor.execute(""" COPY your_app_nela (name, last, almost, number) FROM %s WITH (FORMAT csv, DELIMITER ',', QUOTE '"', HEADER); """, [csv_filepathname])
MySQL:LOAD DATA INFILE命令
from django.db import connection def insert(request): csv_filepathname = "your_file_path.csv" with connection.cursor() as cursor: # 替换成你的实际表名和列名,有表头就加IGNORE 1 ROWS cursor.execute(f""" LOAD DATA INFILE '{csv_filepathname}' INTO TABLE your_app_nela FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS (name, last, almost, number); """)
注意事项
- 确保数据库用户有权限访问CSV文件路径(PostgreSQL的COPY需要文件在数据库服务器可访问路径,或者用
COPY FROM STDIN;MySQL可以加LOCAL参数读取客户端文件,但需要配置允许)。 - 导入前可以临时删除表的索引和约束,导入完成后再重建——索引会大幅减慢插入速度,百万级数据尤其明显。
额外优化建议
- 关闭模型信号:如果你的
Nela模型有post_save/pre_save等信号,bulk_create不会触发这些信号,若不需要可以临时断开,避免额外开销。 - 用
csv.DictReader替代csv.reader:如果CSV有表头,可以直接用列名映射,代码更易读。
内容的提问来源于stack exchange,提问作者michael93pl
相关产品推荐
相关产品推荐

