【问题标题】:In Django, how to achieve repeatable reads for a transaction?在 Django 中,如何实现事务的可重复读取?
【发布时间】:2015-09-06 11:24:06
【问题描述】:

我有一个函数,它对同一个数据集执行多个查询,我想确保所有查询都会看到完全相同的数据。

就 SQL 而言,这意味着支持它的数据库的 REPEATABLE READ 隔离级别。如果数据库没有能力,我不介意更高级别甚至完全锁定。

据我所知,情况并非如此。 IE。如果我在一个 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 有一些兼容性,在这种情况下,即使完全锁定整个数据库也可以。然而,显然,解决方案越通用,就越受欢迎。

【问题讨论】:

  • 缓存此元数据是一种可接受的方法吗?
  • 不幸的是,没有。我所做的查询计算原始事件数据的各种统计信息并获得一致的视图,我必须将整个数据集拉到内存中,这是我真的不想做的事情。

标签: python django transactions isolation-level transaction-isolation


【解决方案1】:

你是对的,postgres 中的默认事务隔离级别是 READ COMMITTED。 您可以在设置中轻松更改它以测试它是否适合您的需求: 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

另外,我建议您先更改设置中的默认模式(如果可以的话)。 然后,如果符合您的需要,您可以将其删除并在特殊位置修改代码。

【讨论】:

  • 感谢您的回复。我之前遇到过性能问题(我认为这是大约五年前),将默认隔离级别从 SERIALIZABLE 更改为 READ COMMITTED(非 Django 项目)对性能产生了合理的影响。也许这只是我的特殊情况,或者我遇到了一些错误。无论哪种方式,我都不愿意在全局范围内设置过于严格的隔离级别,并且真的很想知道是否有一种方法可以在单个函数或代码块的执行期间更改它。或者知道这是不可行的,因为 ORM 设计中的某些东西阻止了这一点。
  • 当然,在某些情况下,严格的隔离模式是多余的。无论如何,postgres 会尽力而为 :) 为代码的特定部分设置一些非默认隔离级别是绝对可以的。在整个项目中使用同一个会更加一致,因为它可以确保您至少不会忘记在某处进行。用代码示例更新了答案。
  • 问题是你必须在任何查询之前调用SET TRANSACTION ISOLATION LEVEL REPEATABLE READ,而且在 Django 中完成并不简单
  • 可能你可以定义两个不同隔离级别到同一个数据库的独立连接,并在需要时使用更受限制的连接?
  • 需要注意的较高隔离级别的主要问题不是隔离级别的开销(它们在 Postgres 上几乎可以忽略不计),而是额外的阻塞(如果检测到 Postgres可能的写冲突)和序列化异常,如果 Postgres 决定并发事务由于刚刚提交的写冲突而不能再继续,则您可能会在应用程序中遇到序列化异常。如果您的应用程序没有冲突,则开销应该可以忽略不计。
猜你喜欢
  • 1970-01-01
  • 2018-04-10
  • 2012-07-29
  • 1970-01-01
  • 1970-01-01
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多