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 的性能吗?
【问题讨论】:
尝试为market
和exchange
字段添加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 批量更新/插入性能的主要内容,如果未能解决你的问题,请参考以下文章