kon*_*ong 16 database django locking atomic
当你这样做时:
@transaction.atomic
def update_db():
do_bulk_update()
Run Code Online (Sandbox Code Playgroud)
在函数运行时,是否锁定数据库?
我问的是关于django的原子事务:https: //docs.djangoproject.com/en/1.10/topics/db/transactions/#autocommit-details
dap*_*azz 25
(我在这个答案中假设现代SQL数据库.)
事务不是锁,而是保持在操作期间自动获取的锁.并且django默认不添加任何锁定,因此答案是否,它不会锁定数据库.
例如,如果你这样做:
@transaction.atomic
def update_db():
cursor.execute('UPDATE app_model SET model_name TO 'bob' WHERE model_id = 1;')
# some other stuff...
Run Code Online (Sandbox Code Playgroud)
您将app_model在"其他内容"的持续时间内锁定ID为1 的行.但是在查询之前它不会被锁定.因此,如果您想确保一致性,您应该明确使用锁.
如上所述,交易不是锁定,因为这对于性能而言是可怕的.通常,它们在第一种情况下是轻量级机制,用于确保如果您对数据库的其他用户进行一次无意义的更改,则这些更改似乎一次全部发生.即原子.事务不会阻止其他用户改变数据库,实际上通常不会阻止其他用户突变您可能正在读取的相同行.
有关如何保护事务的更多详细信息,请参阅本指南和数据库文档(例如postgres).
当您使用atomic装饰器时(参考代码),Django本身会执行以下操作.
禁用自动提交.Autocommit是一个应用程序级别的功能,它将始终立即提交事务,因此它会向应用程序查找,因为从未有过未完成的事务.
这告诉数据库启动一个新事务.
此时psycopg2,postgres将事务的隔离级别设置为READ COMMITTED,这意味着事务中的任何读取都只返回已提交的数据,这意味着如果另一个事务写入,则在提交之前不会看到该更改.这确实意味着,如果该事务在您的交易期间提交,您可以再次阅读,并在交易期间看到该值已更改.
显然这意味着数据库没有被锁定.
运行你的代码.您提交的任何查询/突变都不会被提交.
基本上在这种情况下我们尝试使用保存点,因此我们可以恢复到"如果我们"回滚"事务",但就数据库连接而言,我们处于同一事务中.
如上所述,数据库可能会为您的事务提供一些自动锁定,如本文档所述.为了演示这一点,请考虑以下代码在postgres数据库上运行,其中包含一个表和一行:
my_table
id | age
---+----
1 | 50
Run Code Online (Sandbox Code Playgroud)
然后你运行这段代码:
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()
Run Code Online (Sandbox Code Playgroud)
你得到:
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)]
Run Code Online (Sandbox Code Playgroud)
并且重点是第二个进程在第一个命令完成之前启动,但是在它调用update命令之后,所以第二个进程必须等待锁定,这就是我们sleep after update age to 100直到commit99 年之后才看到的原因.
如果你把睡眠放在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)]
Run Code Online (Sandbox Code Playgroud)
指示锁定在第二个进程更新时未获取,该更新首先发生,但在第一个进程的事务期间发生.
正如 @daphtdazz 答案所述,当您打开事务时,Django 不会获取任何锁,但当您更新数据时,数据库可能会获取自动锁。锁的类型和范围取决于数据库,并且还可能取决于事务隔离级别。有关这些自动锁定的详细信息,请参阅数据库文档。
如果您想手动锁定,有几个选项。
主要也是最简单的一个是进行select_for_update()查询。这将获取一个更新锁,该锁将阻止对与查询匹配的行的所有其他更新。这与您在事务中更新行时自动获取的锁相同,但select_for_update()允许您在实际进行更新之前获取更新锁,这通常很有用。
如果行锁定不适合您的情况,您可以在支持它们的数据库(例如Postgres)中获取咨询锁。开箱即用,Django 不支持此功能,但有第三方软件包向 Django 添加了对咨询锁的支持,或者您可以简单地发出适当的原始 SQL 查询。
| 归档时间: |
|
| 查看次数: |
4755 次 |
| 最近记录: |