【问题标题】:Django Mass Update/Insert PerformanceDjango 批量更新/插入性能
【发布时间】: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() 的(轻微)优化。

标签: django python-3.x django-models django-database


【解决方案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 来防止注入攻击。
猜你喜欢
  • 2012-02-16
  • 2013-07-12
  • 2011-02-28
  • 2014-09-17
  • 2012-01-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多