django中实现事务的几种方式
具体表现形式为:每次数据库操作(比如调用save()方法)会立即被提交到数据库中。
但是如果你希望把连续的SQL操作包裹在一个事务里,就需要手动开启事务
根据粒度不同,三种
全局:
全局,每次请求在一个事务中,粒度太大,事务时间很长
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'llll', 'HOST': '127.0.0.1', 'PORT': '3306', 'USER': 'llll', 'PASSWORD': '123', #全局开启事务,绑定的是http请求响应整个过程 'ATOMIC_REQUESTS': True, } }
局部禁用全局事务
from django.db import transaction
局部禁用事务
@transaction.non_atomic_requests def seckill(request): return HttpResponse('秒杀成功')
多数据库,用default的视图不受事务控制
@transaction.non_atomic_requests(using='default') def seckill(request): return HttpResponse('秒杀成功')
视图开启事务
# 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
局部使用事务
################ 局部使用事务##################### from django.db import transaction def seckill(request): with transaction.atomic(): save() update() 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) #提交保存点
事务提交后,执行某个回调函数
有时候我们当前事务提交后立即执行额外的任务,比如客户下订单后立即邮件通知卖家
案例1:
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('12345678910')) 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': 'llll', 'PASSWORD': '123', } }
创建数据库
原生mysql悲观锁
begin; # 开启事务 select * from goods where id = 1 for update; # 行锁 # order表中加数据 update goods set stock = stock - 1 where id = 1; # 更新 commit; #提交事务
1、使用悲观锁实现
#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('库存不足,秒杀失败')
乐观锁秒杀 普通版
@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('库存不足,秒杀失败')
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能