Python 如何在从数据库加载大数据并转换为JSON时提高性能
我写了一个Djano应用程序,它处理财务数据处理。 我必须从MySQL表中加载大数据(超过1000000条记录),并在django视图中将记录转换为JSON数据,如下所示:Python 如何在从数据库加载大数据并转换为JSON时提高性能,python,django,json,python-2.7,django-models,Python,Django,Json,Python 2.7,Django Models,我写了一个Djano应用程序,它处理财务数据处理。 我必须从MySQL表中加载大数据(超过1000000条记录),并在django视图中将记录转换为JSON数据,如下所示: trades = MtgoxTrade.objects.all() data = [] for trade in trades: js = dict() js['time']= trade.time js['price']= trade.price
trades = MtgoxTrade.objects.all()
data = []
for trade in trades:
js = dict()
js['time']= trade.time
js['price']= trade.price
js['amount']= trade.amount
js['type']= trade.type
data.append(js)
return data
import time
ts = time.time()
data = list()
for i in range(1000000):
d = {}
d['a'] = 1
d['b'] = 2
d['c'] = 3
d['d'] = 4
d['a'] = 5
data.append(d)
print(time.time() - ts)
/opt/python-3.3.0/bin/python3 foo2.py
0.5906929969787598
python2.6 foo2.py
1.74390792847
python2.7 foo2.py
0.673550128937
问题是FOR循环非常慢(200000条记录需要9秒以上),有没有有效的方法在Python中将DB记录转换为JSON格式的数据
更新:我已经根据Mike Housky在我的ENV(ActivePython2.7,Win7)中的回答运行了代码,并对代码进行了更改,结果是:
def create_data(n):
from api.models import MtgoxTrade
result = MtgoxTrade.objects.all()
return result
Build ............ 0.330999851227
For loop ......... 7.98400020599
List Comp. ....... 0.457000017166
Ratio ............ 0.0572394796312
For loop 2 ....... 0.381999969482
Ratio ............ 0.047845686326
你会发现for循环大约需要8秒!如果我注释掉For循环,那么List Comp也需要这样的时间:
Times:
Build ............ 0.343000173569
List Comp. ....... 7.57099986076
For loop 2 ....... 0.375999927521
我的新问题是for循环是否会触及数据库?但我没有看到任何数据库访问日志。真奇怪您可以使用列表理解,因为它可以防止许多
dict()
和append()
调用:
trades = MtgoxTrade.objects.all()
data = [{'time': trade.time, 'price': trade.price, 'amount': trade.amount, 'type': trade.type}
for trade in trades]
return data
函数调用在Python中非常昂贵,因此您应该尽量避免在慢循环中调用函数。以下是一些可以尝试的技巧/事项 由于最终需要从queryset生成JSON字符串,请使用django的内置: 通过使用或模块,可以加快序列化速度。请参阅设置 此外,不要从记录中获取所有字段值,而是显式地获取需要序列化的内容:
MtgoxTrade.objects.all().values('time','price','amount','type')
此外,您可能希望使用queryset的方法:
…对于返回大量您需要的对象的QuerySet
只需要访问一次,这样可以获得更好的性能和
显著减少记忆
此外,您还可以将庞大的queryset拆分为多个批次,请参见:
另见:
您还将注意到,Python 2.6和2.7之间存在显著的性能差异。这个答案支持Simeon Visser的观察。我运行了以下代码:
import gc, random, time
if "xrange" not in dir(__builtins__):
xrange = range
class DataObject(object):
def __init__(self, time, price, amount, type):
self.time = time
self.price = price
self.amount = amount
self.type = type
def create_data(n):
result = []
for index in xrange(n):
s = str(index);
result.append(DataObject("T"+s, "P"+s, "A"+s, "ty"+s))
return result
def convert1(trades):
data = []
for trade in trades:
js = dict()
js['time']= trade.time
js['price']= trade.price
js['amount']= trade.amount
js['type']= trade.type
data.append(js)
return data
def convert2(trades):
data = [{'time': trade.time, 'price': trade.price, 'amount': trade.amount, 'type': trade.type}
for trade in trades]
return data
def convert3(trades):
ndata = len(trades)
data = ndata*[None]
for index in xrange(ndata):
t = trades[index]
js = dict()
js['time']= t.time
js['price']= t.price
js['amount']= t.amount
js['type']= t.type
#js = {"time" : t.time, "price" : t.price, "amount" : t.amount, "type" : t.type}
return data
def main(n=1000000):
t0s = time.time()
trades = create_data(n);
t0f = time.time()
t0 = t0f - t0s
gc.disable()
t1s = time.time()
jtrades1 = convert1(trades)
t1f = time.time()
t1 = t1f - t1s
t2s = time.time()
jtrades2 = convert2(trades)
t2f = time.time()
t2 = t2f - t2s
t3s = time.time()
jtrades3 = convert3(trades)
t3f = time.time()
t3 = t3f - t3s
gc.enable()
print ("Times:")
print (" Build ............ " + str(t0))
print (" For loop ......... " + str(t1))
print (" List Comp. ....... " + str(t2))
print (" Ratio ............ " + str(t2/t1))
print (" For loop 2 ....... " + str(t3))
print (" Ratio ............ " + str(t3/t1))
main()
Win7、Core 2 Duo 3.0GHz上的结果:
Python 2.7.3:
Times:
Build ............ 2.95600008965
For loop ......... 0.699999809265
List Comp. ....... 0.512000083923
Ratio ............ 0.731428890618
For loop 2 ....... 0.609999895096
Ratio ............ 0.871428659011
Python 3.3.0:
Times:
Build ............ 3.4320058822631836
For loop ......... 1.0200011730194092
List Comp. ....... 0.7500009536743164
Ratio ............ 0.7352942070195492
For loop 2 ....... 0.9500019550323486
Ratio ............ 0.9313733946208623
即使禁用了GC,它们也会有一些变化(启用GC时变化更大,但结果大致相同)。第三个转换计时显示,节省的时间中有相当大的一部分来自一百万次不调用.append()
忽略“For循环2”次。这个版本有一个bug,我现在没有时间修复它。我认为尝试对数据库进行原始查询是值得的,因为模型将大量额外的样板代码放入字段(我相信字段就是属性),并且像前面提到的函数调用一样,代价高昂。请参阅,页面底部有一个示例,使用dictfetchall,这似乎是您要查找的内容。您可能需要查看。它将返回dicts的iterable,而不是model对象,因此您不必创建大量中间数据结构。你的代码可以简化为
return MtgoxTrade.objects.values('time', 'price', 'amount', 'type')
在这里,列表理解不会给您带来任何显著的加速。可能这取决于实现。在Win7、CPython2.7.3和3.3.0上,我看到for循环中的列表理解能力提高了25%或更多。我会在回答中发布代码。你可以从另一个角度看问题。您可以使用芹菜来异步执行所有昂贵的任务芹菜对异步编程很有好处,但我的案例现在更喜欢同步设计,以后可能会检查:)谢谢~Set
DEBUG=False
并重试。@Burhan Khalid,DEBUG=False无法解决问题。我使用Python2.7,您的示例代码运行不到1秒,虽然我的循环仅在200000条记录下运行了8秒以上?但这在使用GeometryField
时似乎不起作用。我使用过django序列化程序,但似乎也没有提高性能。而我的MtgoxTrade表只有上述4个字段。“trades=MtgoxTrade.objects.all()”只需要不到1秒的时间,但是for循环需要超过8秒!对“MtgoxTrade.objects.all().values('time'、'price'、'amount'、'type')”这种方式确实以20%的比率提高了性能!
return MtgoxTrade.objects.values('time', 'price', 'amount', 'type')