Transaction数据库
事务:一个最小的不可再分的工做单元;一般一个事务对应一个完整的业务(例如银行帐户转帐业务,该业务就是一个最小的工做单元)django
一个完整的业务须要批量的DML(insert、update、delete)语句共同联合完成并发
事务只和DML语句 ( 数据库操做语句 ) 有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不一样,DML语句的个数不一样高并发
▧ 原子性(A) 事务是最小单位,不可再分post
▧ 一致性(C) 事务要求全部的DML语句操做的时候,必须保证同时成功或者同时失败atom
▧ 隔离性(I) 事务A和事务B之间具备隔离性spa
▧ 持久性(D) 是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中)线程
▧ 开启事务 Start Transactioncode
▧ 事务结束 End Transactionblog
▧ 提交事务 Commit Transaction
▧ 回滚事务 Rollback Transaction
任何一条DML语句(insert、update、delete)执行,标志事务的开启
▧ 提交 成功的结束,将全部的DML语句操做历史记录和底层硬盘数据来一次同步
▧ 回滚 失败的结束,将全部的DML语句操做历史记录所有清空
Django 自带的代码库
from django.db import transaction
方式一, 直接使用将一段操做设置为事务
with transaction.atomic():
...
方式二, 装饰器方式
@transaction.atomic def foo(): ....
老是假设最坏的状况,每次取数据时都认为其余线程会修改,因此都会加锁(读锁、写锁、行锁等)
当其余线程想要访问数据时,都须要阻塞挂起。能够依靠数据库实现,如行锁、读锁和写锁等,都是在操做以前加锁
保证同一时刻只有一个线程能操做数据,其余线程则会被 block
▧ 无脏读 上锁数据保证一致, 所以无脏读, 对脏读不容许的环境悲观锁能够胜任
▧ 无并行 悲观锁对事务成功性能够保证, 可是会对数据加锁致使没法实现数据的并行处理.
▧ 事务成功率高 上锁保证一次成功, 所以在对数据处理的成功率要求较高的时候更适合悲观锁.
▧ 开销大 悲观锁的上锁解锁是有开销的, 若是超大的并发量这个开销就不容小视, 所以不适合在高并发环境中使用悲观锁
▧ 一次性完成 若是乐观锁屡次尝试的代价比较大,也建议使用悲观锁, 悲观锁保证一次成功
from django.shortcuts import render from django.http import HttpResponse from django.views.generic import View from django.db import transaction from 应用名.models import 模型类名 # 类视图 (并发,悲观锁) class MyView(View): @transaction.atomic def post(self, request): # select * from 表名 where id=1 for update; # for update 就表示锁,只有获取到锁才会执行查询,不然阻塞等待。 obj = 模型类名.objects.select_for_update().get(id=1) # 等事务提交后,会自动释放锁。 return HttpResponse('ok')
老是认为不会产生并发问题,每次去取数据的时候总认为不会有其余线程对数据进行修改,所以不会上锁
可是在更新时会判断其余线程在这以前有没有对数据进行修改,通常会使用版本号机制或CAS操做实现。
若是发现数据被改了. 就进行事务回滚取消以前的操做
▧ 脏读 乐观锁不涉及到上锁的处理, 所以在数据并行需求的时候是更适合乐观锁,固然会产生脏读, 不过用回滚取消掉了.
▧ 高并发 相比起悲观锁的开销, 乐观锁也是比悲观锁更适合于高并发场景
▧ 事务成功率低 乐观锁不能保证每次事务的成功, 是使用回滚方式来保证数据一致性, 所以会致使事务成功率很低.
▧ 读多写少 乐观锁适用于读多写少的应用场景,这样能够提升并发粒度
▧ 开销小 可能会致使不少次的回滚都不能拿到正确的处理回应, 所以若是对成功性要求低,并且每次开销小比较适合乐观锁
from django.shortcuts import render from django.http import JsonResponse from django.views.generic import View from django.db import transaction from 应用名.models import GoodsSKU # 类视图 (并发,乐观锁) class MyView(View): @transaction.atomic def post(self, request): '''订单建立''' count = 3 # 订购3件商品 # 设置事务保存点 s1 = transaction.savepoint() # 乐观锁,最多尝试5次 for i in range(5): # 查询商品的信息(库存) try: sku = GoodsSKU.objects.get(id=1) except: # 商品不存在 transaction.savepoint_rollback(s1) return JsonResponse({'res': 1, 'errmsg': '商品不存在'}) # 判断商品的库存 if count > sku.stock: transaction.savepoint_rollback(s1) return JsonResponse({'res': 2, 'errmsg': '商品库存不足'}) # 更新商品的库存和销量 orgin_stock = sku.stock # 原库存 (数据库隔离级别必须是Read Committed;若是是Repeatable Read,那么屡次尝试读取的原库存都是同样的,读不到其余线程提交更新后的数据。) new_stock = orgin_stock - count # 更新后的库存 new_sales = sku.sales + count # 更新后的销量 # update 商品表 set stock=new_stock, sales=new_sales where id=1 and stock = orgin_stock # 经过where子句中的条件判断库存是否进行了修改。(并发,乐观锁) # 返回受影响的行数 res = GoodsSKU.objects.filter(id=1, stock=orgin_stock).update(stock=new_stock, sales=new_sales) if res == 0: # 若是修改失败 if i == 4: # 若是尝试5次都失败 transaction.savepoint_rollback(s1) return JsonResponse({'res': 3, 'errmsg': '下单失败'}) continue # 再次尝试 # 不然更新成功 # 跳出尝试循环 break # 提交事务 transaction.savepoint_commit(s1) # 返回应答 return JsonResponse({'res': 4, 'message': '建立成功'})