加快 Django 表单以将大型 (500k obs) CSV 文件上传到 MySQL 数据库
SPEED UP Django Form to Upload large (500k obs) CSV file to MySQL DB
Django table 大约有 430,000 个 obs 和 230mb 文件;\
并且来自下面详细概述的平面 CSV 文件\
MODELS.PY。我考虑过为 CSV Reader 使用块,但我认为处理器\
我有填充 MySQL table 的函数是我的挂断;需要20小时+\
我怎样才能加快速度?
class MastTable(models.Model):
evidence = models.ForeignKey(Evidence, blank=False)
var2 = models.CharField(max_length=10, blank=True, null=True)
var3 = models.CharField(max_length=10, blank=True, null=True)
var4 = models.CharField(max_length=10, blank=True, null=True)
var5 = models.CharField(max_length=10, blank=True, null=True)
var6 = models.DateTimeField(blank=True, null=True)
var7 = models.DateTimeField(blank=True, null=True)
var8 = models.DateTimeField(blank=True, null=True)
var9 = models.DateTimeField(blank=True, null=True)
var10 = models.DateTimeField(blank=True, null=True)
var11 = models.DateTimeField(blank=True, null=True)
var12 = models.DateTimeField(blank=True, null=True)
var13 = models.DateTimeField(blank=True, null=True)
var14 = models.CharField(max_length=500, blank=True, null=True)
var15 = models.CharField(max_length=500, blank=True, null=True)
var16 = models.CharField(max_length=50, blank=True, null=True)
var17 = models.CharField(max_length=500, blank=True, null=True)
var18 = models.CharField(max_length=500, blank=True, null=True)
var19 = models.CharField(max_length=500, blank=True, null=True)
var20 = models.CharField(max_length=500, blank=True, null=True)
var21 = models.CharField(max_length=500, blank=True, null=True)
var22 = models.CharField(max_length=500, blank=True, null=True)
var23 = models.DateTimeField(blank=True, null=True)
var24 = models.DateTimeField(blank=True, null=True)
var25 = models.DateTimeField(blank=True, null=True)
var26 = models.DateTimeField(blank=True, null=True)
此辅助函数将为 CSV 创建一个 reader 对象\
并在 MySQL 上传
之前解码文件中的任何时髦编解码器
def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs):
csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs)
for row in csv_reader:
yield [unicode(cell, 'ISO-8859-1') for cell in row]
UTILS.PY 文件中的一个函数然后将访问一个数据库 table(名为 'extract_properties')\
包含文件头以标识要转到的处理器函数\
处理器功能如下所示
def processor_table(extract_properties): #Process the table into MySQL
evidence_obj, created = Evidence.objects.get_or_create(case=case_obj,
evidence_number=extract_properties['evidence_number']) #This retrieves the Primary Key
reader = unicode_csv_reader(extract_properties['uploaded_file'],dialect='pipes') #CSVfunction
for idx, row in enumerate(reader):
if idx <= (extract_properties['header_row_num'])+3: #Header is not always 1st row of file
pass
else:
try:
obj, created = MastTable.objects.create( #I was originally using 'get_or_create'
evidence=evidence_obj,
var2=row[0],
var3=row[1],
var4=row[2],
var5=row[3],
var6=date_convert(row[4],row[5]), #funct using 'dateutil.parser.parse'
var7=date_convert(row[6],row[7]),
var8=date_convert(row[8],row[9]),
var9=date_convert(row[10],row[11]),
var10=date_convert(row[12],row[13]),
var11=date_convert(row[14],row[15]),
var12=date_convert(row[16],row[17]),
var13=date_convert(row[18],row[19]),
var14=row[20],
var15=row[21],
var16=row[22],
var17=row[23],
var18=row[24],
var19=row[25],
var20=row[26],
var21=row[27],
var22=row[28],
var23=date_convert(row[29],row[30]),
var24=date_convert(row[31],row[32]),
var25=date_convert(row[33],row[34]),
var26=date_convert(row[35],row[36]),)
except Exception as e: #This logs any exceptions to a custom DB table
print "Error",e
print "row",row
print "idx:",idx
SystemExceptionLog.objects.get_or_create(indexrow=idx, errormsg=e.args[0],
timestamp=datetime.datetime.now(),
uploadedfile=extract_properties['uploaded_file'])
continue
return True
最后 VIEWS.PY 下面的表单接受文件并调用上面的处理器来填充数据库
检查有效的表单数据,如果有效
,则将任何文件传递给文件处理程序
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
for _file in request.FILES.getlist('file'):
extract_properties = get_file_properties(_file)
if extract_properties:
for property in extract_properties: #File is found and processor kicked off
print "starting parser"
try:
property['evidence_number'] = request.POST.get('evidence_number')
result = process_extract(property)
if result is None:
print 'Unable to get determine extract properties!'
except Exception as e:
print "!!!!!!!"
print "Error, could not upload", e
pass
else:
print 'Unable to identify file uploaded!'
return HttpResponseRedirect('')
print form
else:
form = UploadFileForm()
return render_to_response('nettop/upload_file.html', # The web frontend Page for Upload
{'form': form},
context_instance=RequestContext(request))
Django中最基本有效的优化就是减少对数据库的查询次数。这对于 100 个查询是正确的,对于 500.000 个查询也是如此。
而不是使用 MastTable.objects.create()
,您应该构建一个未保存模型实例的列表,并使用 MastTable.objects.bulk_create(list_of_models)
在尽可能少的数据库往返中创建它们。这应该会大大加快它的速度。
如果您使用的是 MySQL,您可以增加 max_allowed_packet
设置以允许更大的批次。它的默认值 1MB 非常低。 PostGRESQL 没有硬编码限制。如果您仍然 运行 遇到性能问题,可以切换到 raw SQL statements。创建 500.000 python 个对象可能有点开销。在我最近的一项测试中,使用 connection.cursor
执行完全相同的查询大约快 20%。
将文件的实际处理留给后台进程使用例如Celery,或使用 StreamingHttpResponse
在此过程中提供反馈。
此 csv 文件是否包含无效行?我的意思是你真的需要这条线吗?
except Exception as e: #This logs any exceptions to a custom DB table
如果没有抛出此类错误,那么您应该使用 bulk_create()
而不是 create()
.
另外我建议在单个事务中执行导入。这是一个 巨大 速度助推器:
from django.db import transaction
with transaction.atomic():
processor_table(extract_properties)
Django table 大约有 430,000 个 obs 和 230mb 文件;\ 并且来自下面详细概述的平面 CSV 文件\ MODELS.PY。我考虑过为 CSV Reader 使用块,但我认为处理器\ 我有填充 MySQL table 的函数是我的挂断;需要20小时+\ 我怎样才能加快速度?
class MastTable(models.Model):
evidence = models.ForeignKey(Evidence, blank=False)
var2 = models.CharField(max_length=10, blank=True, null=True)
var3 = models.CharField(max_length=10, blank=True, null=True)
var4 = models.CharField(max_length=10, blank=True, null=True)
var5 = models.CharField(max_length=10, blank=True, null=True)
var6 = models.DateTimeField(blank=True, null=True)
var7 = models.DateTimeField(blank=True, null=True)
var8 = models.DateTimeField(blank=True, null=True)
var9 = models.DateTimeField(blank=True, null=True)
var10 = models.DateTimeField(blank=True, null=True)
var11 = models.DateTimeField(blank=True, null=True)
var12 = models.DateTimeField(blank=True, null=True)
var13 = models.DateTimeField(blank=True, null=True)
var14 = models.CharField(max_length=500, blank=True, null=True)
var15 = models.CharField(max_length=500, blank=True, null=True)
var16 = models.CharField(max_length=50, blank=True, null=True)
var17 = models.CharField(max_length=500, blank=True, null=True)
var18 = models.CharField(max_length=500, blank=True, null=True)
var19 = models.CharField(max_length=500, blank=True, null=True)
var20 = models.CharField(max_length=500, blank=True, null=True)
var21 = models.CharField(max_length=500, blank=True, null=True)
var22 = models.CharField(max_length=500, blank=True, null=True)
var23 = models.DateTimeField(blank=True, null=True)
var24 = models.DateTimeField(blank=True, null=True)
var25 = models.DateTimeField(blank=True, null=True)
var26 = models.DateTimeField(blank=True, null=True)
此辅助函数将为 CSV 创建一个 reader 对象\ 并在 MySQL 上传
之前解码文件中的任何时髦编解码器def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs):
csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs)
for row in csv_reader:
yield [unicode(cell, 'ISO-8859-1') for cell in row]
UTILS.PY 文件中的一个函数然后将访问一个数据库 table(名为 'extract_properties')\ 包含文件头以标识要转到的处理器函数\ 处理器功能如下所示
def processor_table(extract_properties): #Process the table into MySQL
evidence_obj, created = Evidence.objects.get_or_create(case=case_obj,
evidence_number=extract_properties['evidence_number']) #This retrieves the Primary Key
reader = unicode_csv_reader(extract_properties['uploaded_file'],dialect='pipes') #CSVfunction
for idx, row in enumerate(reader):
if idx <= (extract_properties['header_row_num'])+3: #Header is not always 1st row of file
pass
else:
try:
obj, created = MastTable.objects.create( #I was originally using 'get_or_create'
evidence=evidence_obj,
var2=row[0],
var3=row[1],
var4=row[2],
var5=row[3],
var6=date_convert(row[4],row[5]), #funct using 'dateutil.parser.parse'
var7=date_convert(row[6],row[7]),
var8=date_convert(row[8],row[9]),
var9=date_convert(row[10],row[11]),
var10=date_convert(row[12],row[13]),
var11=date_convert(row[14],row[15]),
var12=date_convert(row[16],row[17]),
var13=date_convert(row[18],row[19]),
var14=row[20],
var15=row[21],
var16=row[22],
var17=row[23],
var18=row[24],
var19=row[25],
var20=row[26],
var21=row[27],
var22=row[28],
var23=date_convert(row[29],row[30]),
var24=date_convert(row[31],row[32]),
var25=date_convert(row[33],row[34]),
var26=date_convert(row[35],row[36]),)
except Exception as e: #This logs any exceptions to a custom DB table
print "Error",e
print "row",row
print "idx:",idx
SystemExceptionLog.objects.get_or_create(indexrow=idx, errormsg=e.args[0],
timestamp=datetime.datetime.now(),
uploadedfile=extract_properties['uploaded_file'])
continue
return True
最后 VIEWS.PY 下面的表单接受文件并调用上面的处理器来填充数据库 检查有效的表单数据,如果有效
,则将任何文件传递给文件处理程序def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
for _file in request.FILES.getlist('file'):
extract_properties = get_file_properties(_file)
if extract_properties:
for property in extract_properties: #File is found and processor kicked off
print "starting parser"
try:
property['evidence_number'] = request.POST.get('evidence_number')
result = process_extract(property)
if result is None:
print 'Unable to get determine extract properties!'
except Exception as e:
print "!!!!!!!"
print "Error, could not upload", e
pass
else:
print 'Unable to identify file uploaded!'
return HttpResponseRedirect('')
print form
else:
form = UploadFileForm()
return render_to_response('nettop/upload_file.html', # The web frontend Page for Upload
{'form': form},
context_instance=RequestContext(request))
Django中最基本有效的优化就是减少对数据库的查询次数。这对于 100 个查询是正确的,对于 500.000 个查询也是如此。
而不是使用 MastTable.objects.create()
,您应该构建一个未保存模型实例的列表,并使用 MastTable.objects.bulk_create(list_of_models)
在尽可能少的数据库往返中创建它们。这应该会大大加快它的速度。
如果您使用的是 MySQL,您可以增加 max_allowed_packet
设置以允许更大的批次。它的默认值 1MB 非常低。 PostGRESQL 没有硬编码限制。如果您仍然 运行 遇到性能问题,可以切换到 raw SQL statements。创建 500.000 python 个对象可能有点开销。在我最近的一项测试中,使用 connection.cursor
执行完全相同的查询大约快 20%。
将文件的实际处理留给后台进程使用例如Celery,或使用 StreamingHttpResponse
在此过程中提供反馈。
此 csv 文件是否包含无效行?我的意思是你真的需要这条线吗?
except Exception as e: #This logs any exceptions to a custom DB table
如果没有抛出此类错误,那么您应该使用 bulk_create()
而不是 create()
.
另外我建议在单个事务中执行导入。这是一个 巨大 速度助推器:
from django.db import transaction
with transaction.atomic():
processor_table(extract_properties)