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

百万行数据批量插入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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:31:22