从数据库加载大数据并转换为 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 时如何提高性能的主要内容,如果未能解决你的问题,请参考以下文章

转换 d3 图表以从变量内的 json 加载数据

从外部 .json 文件加载默认数据并存储到 MongoDB

如何使用 spark 从 hbase 读取

如何获取所有mysql元组结果并转换为json

ASP.NET + SQL SERVER + JSON = 如何在 SQL Server 的两个连接表中获取数据并加载为 JSON?

如何在 Java 中处理大数据?