Django 批量更新/插入性能

Posted

技术标签:

【中文标题】Django 批量更新/插入性能【英文标题】:Django Mass Update/Insert Performance 【发布时间】:2018-10-11 06:18:13 【问题描述】:

我每 5 秒收到大约 5000 种工具的财务数据,需要更新数据库中的相应条目。模型如下:

class Market(models.Model):
    market = models.CharField(max_length=200)
    exchange = models.ForeignKey(Exchange,on_delete=models.CASCADE) 
    ask = models.FloatField()
    bid = models.FloatField()
    lastUpdate = models.DateTimeField(default = timezone.now)

需要发生的事情如下:

收到新的财务数据后,检查是否存在条目 数据库。 如果条目存在,请更新 ask、bid 和 lastUpdate 字段 如果该条目不存在,则创建一个新条目

我的代码如下:

bi_markets = []
for item in dbMarkets:
    eItem = Market.objects.filter(exchange=item.exchange,market=item.market)
    if len(eItem) > 0:
        eItem.update(ask=item.ask,bid=item.bid)
    else:
        bi_markets.append(item)

#Bulk insert items that does not exist
Market.objects.bulk_create(bi_markets)  

但是执行此操作需要的时间太长。大约 30 秒。我需要将时间减少到 1 秒。我知道这可以在 100 毫秒内完成,就像我在 .NET 中使用自定义 SQL 代码一样。知道如何提高 Django 的性能吗?

【问题讨论】:

尝试为marketexchange 字段添加db_index=True 为什么不使用exists = Model.objects.filter(category='django').exists()将有助于减少一次检查并尝试使用值获取dbmatrkets。我的意思是像这样dbmarkets = Model.objects.value('value1','value2')。这将减少您的查询时间。 添加索引并没有提高性能 使用 exists() 将增加额外的运行时间层,因为 99.9% 的项目将存在于表中 不,不会的。这将是对调用COUNT()len() 的(轻微)优化。 【参考方案1】:

如果你想要的是这种性能,我不明白你为什么不直接使用原始 SQL。批量创建尚不存在的东西听起来像是 Django 不适合的高级 SQL 查询。

https://docs.djangoproject.com/en/2.0/topics/db/sql/

您也可以这样做(抱歉在移动设备上):

bi_markets = []
for item in dbMarkets:
  rows = Market.objects.filter(exchange=item.exchange, market=item.market).update(ask=item.ask, bid=item.bid)
  if rows == 0:
    bi_markets.append(item)

Market.objects.bulk_create(bi_markets)

也许这种组合会生成一些更好的 SQL,并且它也回避了 exists() 调用(update 返回它改变了多少行)。

【讨论】:

谢谢。问题不在于批量插入。它正在检查项目是否存在并更新它。如果我删除所有项目并批量插入它就足够快了,但问题是PK显然每次都会改变。需要“批量更新”功能,可以为每个项目提供不同的更新 我扩展了它,也许这会执行?至少它更整洁。【参考方案2】:

我决定拆分更新并创建功能。创建仅在应用程序启动时发生,从那里我使用自定义 SQL 脚本进行更新。见下文。工作得很好。

updateQ = []
updateQ.append("BEGIN TRANSACTION;")

for dbItem in dbMarkets:
    eItem = tickers[dbItem.market]
    qStr = "UPDATE app_market SET ask = " + str(eItem['ask']) + ",bid = " + str(eItem['bid']) + " WHERE exchange_id = " + str(e.dbExchange.pk) + " AND market = " + '"' + dbItem.market + '";'
    updateQ.append(qStr)

updateQ.append("COMMIT;")

updateQFinal  = ''.join(map(str, updateQ))

with connection.cursor() as cursor:
    cursor.executescript(updateQFinal)

【讨论】:

这是真的吗?至少阅读有关原始 SQL 的 Django 文档以及它们如何使用 %s 来防止注入攻击。

以上是关于Django 批量更新/插入性能的主要内容,如果未能解决你的问题,请参考以下文章

Django 批量插入数据自定义分页器多表关系的建立及Form组件(待更新。。。)

数据库插入或者更新大批量数据的性能优化

如何强制 django 立即保存而不是在循环后进行批量更新

为啥批量插入/更新更快?批量更新如何工作?

mysql批量更新数据(性能优化) 第一种方式

mybatis批量插入数据性能测试