ORM中的事务和锁
# 1. 事务
事务要确保原子性
"""
事务
ACID
原子性:不可分隔的最小单位
一致性:跟原子性是相辅相成
隔离性:事务之间相互不干扰
持久性:事务一旦确认永久生效
事务的回滚
rollback
事务的确认
commit
"""
# 目前是需要
from django.db import transaction
with transaction.atomic(): 开启事务
# sql1
# sql2
# 的with代码块中写的所有orm操作都是属于同一个事务
print("执行其他操作")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
import django
django.setup()
from app01 import models
from django.db import transaction
from django.db.models import F, Q
# 事务要确保原子性
# 原子性:一个原子事务要么完整执行,要么干脆不执行。这意味着,工作单元中的每项任务都必须正确执行。如果有任一任务执行失败,则整个工作单元或事务就会被终止。
try:
with transaction.atomic(): # 开启事务
# 一系列的操作
models.Book.objects.all().update(kucun=F("kucun") - 10)
models.Book.objects.all().update(sale=F("sale") + 10)
except Exception as e:
print(e)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
下面再说一些设置事务的小原则吧:
1.保持事务短小 2.尽量避免事务中rollback 3.尽量避免savepoint 4.默认情况下,依赖于悲观锁 5.为吞吐量要求苛刻的事务考虑乐观锁 6.显示声明打开事务 7.锁的行越少越好,锁的时间越短越好
# 2. 锁
# 2.1 行级锁
entries = Entry.objects.select_for_update().filter(author=request.user) #加互斥锁,由于mysql在查询时自动加的是共享锁,所以我们可以手动加上互斥锁。create、update、delete操作时,mysql自动加行级互斥锁
SELECT * FROM t1 WHERE id=1 FOR UPDATE;
model.T1.objects.select_for_update().filter(id=1)
1
2
3
4
2
3
4
# 2.2 表锁
class LockingManager(models.Manager):
""" Add lock/unlock functionality to manager.
Example::
class Job(models.Model): #其实不用这么负载,直接在orm创建表的时候,给这个表定义一个lock和unlock方法,借助django提供的connection模块来发送锁表的原生sql语句和解锁的原生sql语句就可以了,不用外层的这个LckingManager(model.Manager)类
manager = LockingManager()
counter = models.IntegerField(null=True, default=0)
@staticmethod
def do_atomic_update(job_id)
''' Updates job integer, keeping it below 5 '''
try:
# Ensure only one HTTP request can do this update at once.
Job.objects.lock()
job = Job.object.get(id=job_id)
# If we don't lock the tables two simultanous
# requests might both increase the counter
# going over 5
if job.counter < 5:
job.counter += 1
job.save()
finally:
Job.objects.unlock()
"""
def lock(self):
""" Lock table.
Locks the object model table so that atomic update is possible.
Simulatenous database access request pend until the lock is unlock()'ed.
Note: If you need to lock multiple tables, you need to do lock them
all in one SQL clause and this function is not enough. To avoid
dead lock, all tables must be locked in the same order.
See http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
"""
cursor = connection.cursor()
table = self.model._meta.db_table
logger.debug("Locking table %s" % table)
cursor.execute("LOCK TABLES %s WRITE" % table)
row = cursor.fetchone()
return row
def unlock(self):
""" Unlock the table. """
cursor = connection.cursor()
table = self.model._meta.db_table
cursor.execute("UNLOCK TABLES")
row = cursor.fetchone()
return row
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
编辑 (opens new window)