7

我有一个函数,在同一个数据集上执行多个查询,我想确保所有查询都能看到完全相同的数据。在Django中,如何实现交易的可重复读取?

在SQL而言,这意味着为支持它的数据库重复读隔离级别。如果数据库不可用,我不介意有更高的级别,甚至完全锁定。

据我看到的,这是情况并非如此。即如果我在一个Python的shell中运行这样的代码:只要

with transaction.atomic(): 
    for t in range(0, 60): 
     print("{0}: {1}".format(t, MyModel.objects.count())) 
     time.sleep(1) 

正如我在另做MyModel.objects.create(...),该值通过立即运行循环增加可见。这正是我想要避免的。进一步的测试显示行为符合READ COMMITTED级别,这对我的口味来说过于宽松。

我想也想强调的一点,我想只为单一功能的更严格的隔离级别,而不是整个项目。

什么是实现这一目标我最好的选择吗?

在我的具体情况,我关心的唯一数据库PostgreSQL的是9.3+,但我也想用sqlite3的一些兼容性在这种情况下甚至完全锁定整个数据库是跟我没关系。然而,显然,解决方案越一般,越优选。

+0

缓存这个元数据是一种可接受的方法? –

+0

不幸的是,没有。我所做的查询计算了原始事件数据的各种统计数据,并且具有一致的视图,我将不得不将整个数据集拉到内存中,这是我真正不想做的事情。 – drdaeman

回答

6

你说的没错,在Postgres的默认事务隔离级别读取已提交。 您可以轻松地更改设置来测试它是否适合你的需求: https://docs.djangoproject.com/en/1.8/ref/databases/#isolation-level

而且我怀疑,因为Postgres的非常高效运行与交易工作时,你会面临一些性能问题。即使在SERIALIZABLE模式下。另外mysql具有REPEATABLE READ默认隔离级别,并且我们发现它也不会影响性能。

反正你可以手动设置隔离模式时,你需要这样的: http://initd.org/psycopg/docs/extensions.html#isolation-level-constants

要设置自定义的事务隔离级别,你可以尝试水木清华这样的:

from django.db import connection 

with transaction.atomic(): 
    cursor = connection.cursor() 
    cursor.execute('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ') 
    # logic 

此外,我建议你更改缺省首先设置模式(如果可以的话)。 然后,如果将符合您的需求,您可以删除它,并在特殊的地方修改代码。

+0

感谢您的回复。我之前遇到过性能问题(我认为这是大约五年前),将默认隔离级别从SERIALIZABLE更改为READ COMMITTED(这是非Django项目)对性能有合理的影响。也许这只是我的特殊情况,或者我遇到了一些错误。无论哪种方式,我都不愿意在全局范围内设置过于严格的隔离级别,并且真的想知道是否有一种方法可以在执行单个函数或代码块期间进行更改。或者要知道这是不可行的,因为ORM设计中的某些东西阻止了这一点。 – drdaeman

+0

当然,有些情况下严格的隔离模式是矫枉过正的。无论如何,postgres将尽其所能:)为代码的特定部分设置一些非默认的隔离级别绝对没问题。在整个项目中使用同一个项目更加一致,因为它确保您至少不会忘记在某个地方完成任务。用代码示例更新了答案。 – alTus

+1

问题是你必须在任何查询之前调用'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ',并且在Django中做起来并不简单 –