1.事务的四大特性
(1)原子性:事务中的所有操作是一个不可分割的整体
(2)一致性:数据变更要保持一致
(3)隔离性:多个事务对数据的修改操作,对彼此间的可见性
(4)持久性,提交后保存到硬盘永久存储
2.事务的隔离级别
事务有隔离性,根据隔离级别不同,事务彼此间的可见性不同
从高到低依次为串行事务(Serializable)、可重复读(Repeatable-Read)、读已提交(Read-Committed)、读未提交(Read-Uncommitted)
2.1 四种级别对比
2.2 修改MySQL的隔离级别
打开MySQL的配置文件,例如
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
在文件尾部追加transaction-isolation,注意级别用大写字母,例如
# 隔离级别越高数据越安全,越低
transaction-isolation=READ-COMMITTED
重启MySQL
sudo /etc/init.d/mysql restart
3.Django事务的使用方法
3.1 导包
Django提供了一个transaction模块来管理事务
from django.db import transaction
3.2 开启事务
有两种开启事务的方法,装饰器和with语句
from django.db import transaction
# 方法一:装饰器
@transaction.atomic
def viewfunc(request):
# 这些代码会在一个事务中执行
......
# 方法二:with语句
def viewfunc(request):
# 这部分代码不在事务中
......
with transaction.atomic():
# 这部分代码会在事务中执行
......
使用装饰器虽然简单便捷,但是会把整个函数都当做事务,这样有时候会很不方便,所以在项目中使用with语句的方法显得更灵活
3.3 事务操作
事务的操作包括提交、回滚,在此之前得先有个保存点来记录数据的状态
from django.db import transaction
# 1.使用with或装饰器开启事务
with transaction.atomic()
# 2.创建保存点
save_id = transaction.savepoint()
# 3.回滚到保存点
transaction.savepoint_rollback(save_id)
# 4.提交事务
transaction.savepoint_commit(save_id)
4.举例
比如说用户下单购买商品
class OrderCommitView(View):
def post(self, request):
......
from django.db import transaction
# 开始事务,使用with语句
with transaction.atomic():
# 创建事务保存点
save_id = transaction.savepoint()
try:
order = OrderInfo.objects.create(
...
)
...
for sku_id in sku_ids:
sku = SKU.objects.get(id=sku_id)
sku_count = carts_dict[sku_id]['count']
if sku_count > sku.stock:
# 库存不足,事务回滚到之前的保存点
transaction.savepoint_rollback(save_id)
return JsonResponse({
'code': 400, 'errmsg': '库存不足'})
...
order.total_amount += order.freight
order.save()
except:
# 若出现异常,回滚事务到保存点
transaction.savepoint_rollback(save_id)
return JsonResponse({
'code': 0, 'errmsg': '下单失败'})
# 若一切正常,提交保存事务
transaction.savepoint_commit(save_id)
...
return JsonResponse({
'code': 0, 'errmsg': '下单成功', 'order_id': order.order_id})
5.乐观锁与悲观锁
- 悲观锁
就是一种对数据的修改持有悲观态度的并发控制方式,总认为自己在执行过程中会被人介入,所以会上锁。传统的关系型数据库一般都会在操作前上锁,比如说行锁,表锁等,读锁,写锁等
悲观锁又分为共享锁和排他锁
共享锁(读锁即S锁)就是多个事务共享,但只能读不能改,排他锁(写锁即X锁)就是不能与其他锁共存
- 乐观锁
乐观锁并非真的上锁,而是对操作持乐观态度,认为在操作的时候不会被介入。并非真的锁,只是在写之前先查一下数据是否与之前读的一样,若一样则说明没有被修改,若不一样则已被修改
我们使用while循环完成乐观锁业务
class OrderCommitView(LoginRequiredJSONMixin, View):
def post(self, request):
...
# 开启事务
with transaction.atomic():
# 设置保存带你
save_id = transaction.savepoint()
order = OrderInfo.objects.create(
...
)
for sku_id in sku_ids:
# 开启乐观锁机制
while True:
# 购物车中的sku商品
sku = SKU.objects.get(pk=sku_id)
# (1)、读旧的库存和销量
old_stock = sku.stock
old_sales = sku.sales
count = cart_dict[sku_id]['count']
if count > old_stock:
# 库存不足, 回滚事务
transaction.savepoint_rollback(save_id)
return JsonResponse({
'code': 400, 'errmsg': '库存不足'}, status=400)
# (2)、基于旧库存和销量计算新值
new_stock = old_stock - count
new_sales = old_sales + count
# (3)、在旧库存和销量基础上,查询后修改
ret = SKU.objects.filter(
pk=sku.id,
stock=old_stock,
sales=old_sales
).update(stock=new_stock, sales=new_sales)
# 如果update函数返回值为0,说明filter过滤结果为空,说明根据旧数据找不到原有的sku,即有别的事务介入
# 如果ret不为0,说明正确更新,跳出循环
if ret:
break
# 中间表OrderGoods表中插入一条数据,表示新建的订单中的sku商品
OrderGoods.objects.create(
...
)
...
order.total_amount += freight
order.save()
# 若一切正常,提交保存点(删除保存点)
transaction.savepoint_commit(save_id)
return JsonResponse({
'code': 0, 'errmsg': 'ok', 'order_id': order_id })
关于MySQL事务操作可以查看【mysql】mysql引擎和事务