gpt4 book ai didi

django - Django Atomic Transaction 是否锁定数据库?

转载 作者:太空狗 更新时间:2023-10-30 01:41:34 26 4
gpt4 key购买 nike

当你这样做时:

@transaction.atomic
def update_db():
do_bulk_update()

函数运行时,是否锁定数据库?

我问的是关于 django 的原子事务: https://docs.djangoproject.com/en/1.10/topics/db/transactions/#autocommit-details

最佳答案

(我假设在这个答案中使用现代 SQL 数据库。)

tl;dr

事务不是锁,而是持有在操作过程中自动获取的锁。而django默认不加任何锁,所以答案是否定的,它不加锁数据库。

例如如果你这样做:

@transaction.atomic
def update_db():
cursor.execute('UPDATE app_model SET model_name TO 'bob' WHERE model_id = 1;')
# some other stuff...

您将在“其他内容”期间锁定 ID 为 1 的 app_model 行。但是直到那个查询它才被锁定。因此,如果您想确保一致性,您应该显式使用锁。

交易

如前所述,事务不是锁,因为这会影响性能。一般来说,它们首先是轻量级机制,以确保如果您进行大量更改,而这些更改对数据库的其他用户来说一次没有意义,那么这些更改似乎会同时发生。 IE。是原子的。事务不会阻止其他用户改变数据库,实际上通常不会阻止其他用户改变您可能正在阅读的相同行。

参见 this guide和您的数据库文档(例如 postgres )以获取有关如何保护事务的更多详细信息。

Atomic 的 Django 实现。

当您使用 atomic 装饰器(指 the code )时,Django 本身会执行以下操作。

还没有在原子 block 中

  1. 禁用自动提交。自动提交是一种应用程序级别的功能,它总是会立即提交事务,因此在应用程序看来似乎永远没有未完成的事务。

    这告诉数据库开始一个新事务。

    • 此时psycopg2 for postgres 将事务的隔离级别设置为READ COMMITTED ,这意味着事务中的任何读取都只会返回已提交的数据,这意味着如果另一个事务写入,在提交之前您不会看到该更改。这确实意味着如果该事务在您的事务期间提交,您可能会再次阅读并看到该值在您的事务期间发生了变化。

      显然这意味着数据库没有被锁定。

  2. 运行您的代码。您所做的任何查询/变更都不会提交。

  3. 提交事务。

  4. 重新启用自动提交。

在较早的原子 block 中

基本上在这种情况下,我们尝试使用保存点,以便在我们“回滚”“事务”时可以恢复到它们,但就数据库连接而言,我们处于同一事务中。

自动锁定

如前所述,数据库可能会为您的事务提供一些自动锁定,如 this doc 中所述。 .为了证明这一点,请考虑以下代码,该代码在一个表和其中的一行的 postgres 数据库上运行:

my_table
id | age
---+----
1 | 50

然后你运行这段代码:

import psycopg2 as Database
from multiprocessing import Process
from time import sleep
from contextlib import contextmanager


@contextmanager
def connection():
conn = Database.connect(
user='daphtdazz', host='localhost', port=5432, database='db_test'
)
try:
yield conn
finally:
conn.close()

def connect_and_mutate_after_seconds(seconds, age):

with connection() as conn:
curs = conn.cursor()
print('execute update age to %d...' % (age,))
curs.execute('update my_table set age = %d where id = 1;' % (age,))
print('sleep after update age to %d...' % (age,))
sleep(seconds)
print('commit update age to %d...' % (age,))
conn.commit()


def dump_table():
with connection() as conn:
curs = conn.cursor()
curs.execute('select * from my_table;')
print('table: %s' % (curs.fetchall(),))

if __name__ == '__main__':

p1 = Process(target=connect_and_mutate_after_seconds, args=(2, 99))
p1.start()

sleep(0.6)
p2 = Process(target=connect_and_mutate_after_seconds, args=(1, 100))
p2.start()
p2.join()

dump_table()

p1.join()

dump_table()

你得到:

execute update age to 99...
sleep after update age to 99...
execute update age to 100...
commit update age to 99...
sleep after update age to 100...
commit update age to 100...
table: [(1, 100)]
table: [(1, 100)]

关键是第二个进程在第一个命令完成之前启动,但在调用 update 命令之后,所以第二个进程必须等待锁定,这就是我们不这样做的原因直到 commit 年龄 99 之后,才会看到 sleep after update age to 100

如果你把 sleep 放在 exec 之前,你会得到:

sleep before update age to 99...
sleep before update age to 100...
execute update age to 100...
commit update age to 100...
table: [(24, 3), (100, 2)]
execute update age to 99...
commit update age to 99...
table: [(24, 3), (99, 2)]

表示在第二个进程进行更新时未获取锁,这首先发生但在第一个进程的事务期间。

关于django - Django Atomic Transaction 是否锁定数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42520917/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com