从数据库加载大数据并转换为 JSON 时如何提高性能
Posted
技术标签:
【中文标题】从数据库加载大数据并转换为 JSON 时如何提高性能【英文标题】:How to improve performance when load large data from database and convert to JSON 【发布时间】:2013-09-25 18:41:23 【问题描述】:我编写了一个处理财务数据处理的 Djano 应用程序。 我必须从 mysql 表中加载大数据(超过 1000000 条记录),并将记录转换为 django 视图中的 JSON 数据,如下所示:
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
问题是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循环是否会触及数据库?但我没有看到任何数据库访问日志。好奇怪!
【问题讨论】:
你可以换个角度看问题。您可以使用 Celery 异步完成所有昂贵的任务 Celery 适合异步编程,但我的情况现在更喜欢同步设计,也许以后检查一下:)谢谢~ 设置DEBUG = False
再试一次。
@Burhan Khalid,DEBUG=False 无法解决问题。
【参考方案1】:
这里有几个提示/可以尝试的方法。
由于您最终需要从查询集中生成 JSON 字符串,因此请使用 django 的内置 serializers:
from django.core import serializers
data = serializers.serialize("json",
MtgoxTrade.objects.all(),
fields=('time','price','amount','type'))
您可以使用ujson 或simplejson 模块加快序列化速度。请参阅SERIALIZATION_MODULES 设置。
另外,不要从记录中获取所有字段值,而是明确并仅获取您需要序列化的内容:
MtgoxTrade.objects.all().values('time','price','amount','type')
另外,您可能想使用查询集的iterator() 方法:
...对于返回大量对象的查询集 只需要访问一次,这可以带来更好的性能和 显着减少内存...
此外,您可以将庞大的查询集拆分为多个批次,请参阅:Batch querysets。
另见:
Why is iterating through a large Django QuerySet consuming massive amounts of memory? Memory efficient Django Queryset Iterator django: control json serialization【讨论】:
很遗憾,但是当您使用GeometryField
时,这似乎不起作用。刚刚面对one such situation today。
我使用过 django 序列化程序,但似乎也没有性能提升。而我的 MtgoxTrade 表只有以上 4 个字段。
"trades = MtgoxTrade.objects.all()" 只需要不到 1 秒,但 for 循环需要 8 秒以上!
是的。 “MtgoxTrade.objects.all().values('time','price','amount','type')”这种方式确实以 20% 的比率提升了性能!【参考方案2】:
您可以使用列表解析,因为它可以防止许多 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 中代价高昂,因此您应该避免在慢循环中使用它们。
【讨论】:
列表推导不会给您带来任何显着的加速。 也许这取决于实现。在 Win7、CPython 2.7.3 和 3.3.0 上,我看到 for 循环的列表理解改进了 25% 或更多。我将在答案中发布代码。【参考方案3】:此答案支持 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”的次数。这个版本有一个错误,我现在没有时间修复它。
【讨论】:
【参考方案4】:首先,您必须检查从数据库或循环内部获取数据时是否发生性能损失。
没有真正的选择可以显着加快速度 - 也没有使用上面提到的列表推导。
但是,Python 2 和 3 之间的性能存在巨大差异。
一个简单的基准测试告诉我,使用 Python 3.3 的 for 循环大约快 2.5 倍(使用一些简单的基准测试,如下所示):
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
您还会注意到 Python 2.6 和 2.7 之间存在显着的性能差异。
【讨论】:
我用的是 Python2.7,你的示例代码运行时间不到 1 秒,而我的循环运行时间超过 8 秒,只有 200000 条记录?【参考方案5】:我认为尝试对数据库进行原始查询是值得的,因为模型会将大量额外的样板代码放入字段中(我相信字段是属性),并且就像前面提到的函数调用一样昂贵。请参阅documentation,页面底部有一个使用 dictfetchall 的示例,这似乎是您所追求的。
【讨论】:
【参考方案6】:您可能想查看values method。它将返回一个可迭代的 dicts 而不是模型对象,因此您不必创建很多中间数据结构。您的代码可以简化为
return MtgoxTrade.objects.values('time', 'price', 'amount', 'type')
【讨论】:
以上是关于从数据库加载大数据并转换为 JSON 时如何提高性能的主要内容,如果未能解决你的问题,请参考以下文章
从外部 .json 文件加载默认数据并存储到 MongoDB
ASP.NET + SQL SERVER + JSON = 如何在 SQL Server 的两个连接表中获取数据并加载为 JSON?