通过 select_for_update 记录中的两个 ForeignKey 优化访问
Posted
技术标签:
【中文标题】通过 select_for_update 记录中的两个 ForeignKey 优化访问【英文标题】:Optimizing access through two ForeignKeys from a select_for_update record 【发布时间】:2013-03-15 08:43:14 【问题描述】:我有一个像这样的 Django (1.4.2) 数据模型:
class CoreData(models.Model):
cdid = models.AutoField(primary_key=True,editable=False)
atr1 = # whatever
atr2 = # whatever
class EnvironData(models.Model):
cdid = models.ForeignKey(CoreData)
# etc
class TransactionData(models.Model):
edid = models.ForeignKey(EnvironData)
# etc
我需要一个原子事务,我在其中更新etc
事务数据:
tdo = TransactionData.objects.select_for_update().get(etc=criteria)
# process transaction
# modify tdo object
tdo.save()
到目前为止,一切都很好。但是,在process transaction
的过程中,我需要检查CoreData.atr1
和CoreData.atr2
。
如果我通过tdo.edid.cdid.atr1,2
访问它们,那么我的理解是我会有一个额外的读取数据库查询,因为 Django 会获取丢失的数据。 (老实说,我不能 100% 确定它只是一个;也可能是两个——甚至六个,但我对此表示怀疑。)
另一方面,如果我将select_related()
与select_for_update()
结合使用,我不仅会锁定不需要(也不应该)锁定的数据,还会为tdo.save()
增加开销。
第三种方法可能是通过独立查询(与tdo
无关)获取数据,如果使用select_related()
,这将保证是单个数据库查询。另外,它可以使用values()
。
我认为最后一种方法最有效,还因为查询可以从EnvironData
对象开始,我已经拥有edid
作为tdo.edid_id
。
我的观点合理吗?还有更好的方法吗?
更新: 完全可以独立访问tdo.edid.cdid.atr1,2
,甚至可以在交易期间更改它们,因为它们彼此独立,并且不需要它们始终保持其价值交易。 (谢谢@Uszy Wieloryba)
【问题讨论】:
你最后做了什么?我遇到过类似的情况,当我尝试将select_for_update
与select_related
一起使用时,我在Postgres 中遇到了一个错误,更不用说我也担心它的性能(在我最常用的方法之一中需要锁定)广泛使用的功能)...这是棘手的东西:/
@orokusaki 是的,这是棘手的东西。我必须承认我还没有优化它,所以要忍受额外的数据库命中,这目前不是性能问题。我从不喜欢组合的select_related
/select_for_update
方法,感谢您的反馈。那么,看起来应该使用最后一种方法。你试过了吗?
我还没有。不过我的想法是:忘记性能,力求简单。如果它是一个 N+1 问题(选择相关),这很重要,但如果它只是 3-5 个额外的查询,我不会汗流浃背。我宁愿在给定的视图中单独选择每条记录进行更新,也不愿承担“我必须记住在更新 postgres 时对此进行测试”等心理开销。如果它成为一个真正的数据库问题,另一个(更好?)解决方案可能是使用 cache.add(...) 来锁定记录。谷歌“使用 memcached 锁定”。
【参考方案1】:
如果你的# process transaction
依赖于CoreData.atr1
和CoreData.atr2
,也许它们应该被锁定。
【讨论】:
您提到这种可能性是对的。可能存在竞争条件,使得事务代码看到不一致的状态并失败。有趣的是,这两者是相互独立的,只要检查它们中的每一个在交易过程中的任何时间点是否满足特定标准就足够了。诚然,通常情况并非总是如此。以上是关于通过 select_for_update 记录中的两个 ForeignKey 优化访问的主要内容,如果未能解决你的问题,请参考以下文章
Django 的 select_for_update 方法是不是与 update 方法一起使用?
从原子块调用的“select_for_update”仍然是 TransactionManagementError
Django select_for_update 不能在事务之外使用