django中实现事务的几种方式
# 1 全局开启事务---> 全局开启事务,绑定的是http请求响应整个过程
DATABASES = {
'default': {
#全局开启事务,绑定的是http请求响应整个过程
'ATOMIC_REQUESTS': True,
}
}
from django.db import transaction
# 局部禁用事务
@transaction.non_atomic_requests
def seckill(request):
return HttpResponse('秒杀成功')
# 2 一个视图函数在一个事物中
# fbv开启
from django.db import transaction
@transaction.atomic
def seckill(request):
return HttpResponse('秒杀成功')
# cbv开启
from django.db import transaction
from rest_framework.views import APIView
class SeckillAPIView(APIView):
@transaction.atomic
def post(self, request):
pass
# 3 局部使用事务
from django.db import transaction
def seckill(request):
with transaction.atomic():
pass # 都在一个事物中
return HttpResponse('秒杀成功')
事物的回滚和保存点
# 1 普通事务操作(手动操作)
transaction.atomic() # 开启事务
transaction.commit() # 提交事务
transaction.rollback() # 回滚事务
# 2 可以使用上下文管理器来控制(自动操作)
with transaction.atomic(): # 自动提交和回滚
# 3 保存点
-开启事务
干了点事
设置保存点1
干了点事
设置一个保存点2
干了点事
回滚到干完第二个事,回滚到保存点2
'''
在事务操作中,我们还会经常显式地设置保存点(savepoint)
一旦发生异常或错误,我们使用savepoint_rollback方法让程序回滚到指定的保存点
如果没有问题,就使用savepoint_commit方法提交事务
'''
from .models import Book
from django.db import transaction
def seckill(request):
with transaction.atomic():
# 设置回滚点,一定要开启事务
sid = transaction.savepoint()
print(sid)
try:
book = Book.objects.get(pk=1)
book.name = '红楼梦'
book.save()
except Exception as e:
# 如发生异常,回滚到指定地方
transaction.savepoint_rollback(sid)
print('出异常了,回滚')
# 如果没有异常,显式地提交一次事务
transaction.savepoint_commit(sid)
return HttpResponse('秒杀成功')
transaction.atomic() # 开启事务
sid = transaction.savepoint() # 设置保存点
transaction.savepoint_rollback(sid) # 回滚到保存点
transaction.savepoint_commit(sid) #提交保存点
事务提交后,执行某个回调函数
# 有的时候我们希望当前事务提交后立即执行额外的任务,比如客户下订单后立即邮件通知卖家
###### 案例一##################
def send_email():
print('发送邮件给卖家了')
def seckill(request):
with transaction.atomic():
# 设置回滚点,一定要开启事务
sid = transaction.savepoint()
print(sid)
try:
book = Book.objects.get(pk=1)
book.count = book.count-1
book.save()
except Exception as e:
# 如发生异常,回滚到指定地方
transaction.savepoint_rollback(sid)
else:
transaction.savepoint_commit(sid)
#transaction.on_commit(send_email)
transaction.on_commit(lambda: send_sms.delay('1898288322'))
return HttpResponse('秒杀成功')
##### 案例二:celery中使用###
transaction.on_commit(lambda: send_sms.delay('1898288322'))
django实现悲观锁乐观锁案例
# 线上卖图书
-图书表 图书名字,图书价格,库存字段
-订单表: 订单id,订单名字
# 表准备
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField() #
count = models.SmallIntegerField(verbose_name='库存')
class Order(models.Model):
order_id = models.CharField(max_length=64)
order_name = models.CharField(max_length=32)
# 使用mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'lqz',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'lqz',
'PASSWORD': '123',
}
}
# 创建lqz数据库
原生mysql悲观锁
begin; # 开启事务
select * from goods where id = 1 for update; # 行锁
# order表中加数据
update goods set stock = stock - 1 where id = 1; # 更新
commit; #提交事务
orm实现上述
#1 使用悲观锁实现下单
@transaction.atomic # 整个过程在一个事物中---》改两个表:book表减库存,订单表生成记录
def seckill(request):
# 锁住查询到的book对象,直到事务结束
sid = transaction.savepoint() # 保存点
# 悲观锁: select_for_update()
# 加锁了--》行锁还是表锁? 分情况,都有可能
#
book = Book.objects.select_for_update().filter(pk=1).first() # 加悲观锁,行锁,锁住当前行
if book.count > 0:
print('库存可以,下单')
# 订单表插入一条
Order.objects.create(order_id=str(datetime.datetime.now()), order_name='测试订单')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是,就回滚
time.sleep(random.randint(1, 4)) # 模拟延迟
book.count=book.count-1
book.save()
transaction.savepoint_commit(sid) # 提交,释放行锁
return HttpResponse('秒杀成功')
else:
transaction.savepoint_rollback(sid) #回滚,释放行锁
return HttpResponse('库存不足,秒杀失败')
乐观锁秒杀--》库存还有,有的人就没成功
# 2 乐观锁秒杀--普通版
@transaction.atomic
def seckill(request):
# 锁住查询到的book对象,直到事务结束
sid = transaction.savepoint()
book = Book.objects.filter(pk=1).first() # 没加锁
count = book.count
print('现在的库存为:%s' % count)
if book.count > 0:
print('库存可以,下单')
Order.objects.create(order_id=str(datetime.datetime.now()), order_name='测试订单-乐观锁')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是,就回滚
# time.sleep(random.randint(1, 4)) # 模拟延迟
res = Book.objects.filter(pk=1, count=count).update(count=count - 1)
if res >= 1: # 表示修改成功
transaction.savepoint_commit(sid)
return HttpResponse('秒杀成功')
else: # 修改不成功,回滚
transaction.savepoint_rollback(sid)
return HttpResponse('被别人改了,回滚,秒杀失败')
else:
transaction.savepoint_rollback(sid)
return HttpResponse('库存不足,秒杀失败')