如何从大体积DataFrame创建Django模型实例并避免服务器超时
解决方案
方案1:pandas分列加载(改动最小,适配现有逻辑)
这个方案的核心是不一次性加载全量CSV到内存,每次仅读取索引列+单个目标列,处理完即释放内存,不管总列数多少,内存占用始终稳定。
实现代码
import pandas as pd import gc from django.db import reset_queries # 第一步:仅读取CSV表头,拿到所有列名 header_df = pd.read_csv(file_decompressed.open(name_file), nrows=0) all_columns = header_df.columns.tolist() # 索引列是第一列,剩下的都是品牌列 brand_columns = [col for col in all_columns if col != header_df.index.name] # 第二步:逐列加载处理 for col_name in brand_columns: # 每次仅读取索引列+当前品牌列,不加载其他列 df_single = pd.read_csv( file_decompressed.open(name_file), index_col=0, usecols=[header_df.index.name, col_name] ) # 创建对象,触发自定义信号 Juice.objects.create( name=col_name, json_data=df_single[col_name].to_dict() ) # 手动释放内存 del df_single gc.collect() # 清空Django内置的SQL查询缓存,避免内存堆积 reset_queries()
方案2:纯Python原生csv模块实现(完全不依赖pandas)
如果不想引入pandas依赖,可以用Python标准库的csv模块实现,内存占用更低:
实现代码
import csv from django.db import reset_queries # 第一步:读取表头 with file_decompressed.open(name_file, 'r') as f: reader = csv.reader(f) header = next(reader) # 第一列为时间戳索引,剩余为品牌列 timestamp_col = header[0] brand_columns = header[1:] # 第二步:逐列处理 for col_idx, col_name in enumerate(brand_columns, start=1): json_data = {} # 重新遍历CSV,仅取时间戳和当前列的值 with file_decompressed.open(name_file, 'r') as f: reader = csv.DictReader(f) for row in reader: ts = row[timestamp_col] json_data[ts] = int(row[col_name]) # 根据实际数据类型调整转换规则 # 创建对象 Juice.objects.create( name=col_name, json_data=json_data ) # 清空缓存避免内存堆积 reset_queries()
额外优化建议
- 大文件导入逻辑不要直接放在Django请求处理流程中,50万行数据处理耗时较长,很容易触发请求超时,建议改成异步任务后台执行,前端轮询查询处理状态即可。
- 如果JSONB字段不需要实时做精细查询,可对json_data做压缩后存储,进一步降低内存和存储开销。
内容的提问来源于stack exchange,提问作者shadow
相关产品推荐
相关产品推荐

