数据库事务具备四个特征,简称为事务的ACID特性mysql
原子性(Atomicity)、
一致性(Consistency)、
隔离性(Isoation)、
持久性(Durability),
事务的隔离性是指在并发环境中,并发的事务是相互隔离的,
能够理解为多个事务同一时间段对数据库的增删改时是要隔离的
SQL标准中定义了四种数据库事务隔离级别,级别从低到高分别为:sql
读未提交(Read Uncommitted)、-> 脏读
读已提交(Read Committed)、 -> 不可重复读
可重复读(Repeatable Read)、-> 换读
串行化(Serializable)。
在事务的并发操做中会出现脏读、不可重复读、幻读。在事务的并发操做中第二类更新丢失能够经过乐观锁和悲观锁解决。
>: set tx_isolation='READ-UNCOMMITTED';
A事务读取B事务还没有提交的数据,此时若是B事务发生错误并执行回滚操做,那么A事务读取到的数据就是脏数据。就好像本来的数据比较干净、纯粹,此时因为B事务更改了它,这个数据变得再也不纯粹。这个时候A事务当即读取了这个脏数据,但事务B良心发现,又用回滚把数据恢复成原来干净、纯粹的样子,而事务A却什么都不知道,最终结果就是事务A读取了这次的脏数据,称为脏读。数据库
这种状况常发生于转帐与取款操做中django
>: set tx_isolation='READ-COMMITTED';
事务A在执行读取操做,由整个事务A比较大,先后读取同一条数据须要经历很长的时间 。而在事务A第一次读取数据,好比此时读取了小明的年龄为20岁,事务B执行更改操做,将小明的年龄更改成30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和以前的数据不同了,也就是数据不重复了,系统不能够读取到重复的数据,成为不可重复读。并发
>: set tx_isolation='REPEATABLE-READ';
事务A在执行读取操做,须要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操做并提交后,这个时候事务A读取的数据总量和以前统计的不同,就像产生了幻觉同样,无缘无故的多了几条数据,成为幻读。post
>: set tx_isolation='SERIALIZABLE';
(1)不可重复读是读取了其余事务更改的数据,针对insert与update操做atom
解决:使用行级锁,锁定该行,事务A屡次读取操做完成后才释放该锁,这个时候才容许其余事务更改刚才的数据。spa
(2)幻读是读取了其余事务新增的数据,针对insert与delete操做调试
解决:使用表级锁,锁定整张表,事务A屡次读取数据总量以后才释放该锁,这个时候才容许其余事务新增数据。
rest
在更新语句中增长过滤条件,进行版本的判断,能够是这条记录的某个字段值。而后经过影响行数来判断是否更新成功。影响条数为0则更新失败,表明这条记录被其余事务修改了.
# django中使用 事务 + 乐观锁 # 这是一个商城订单提交要修改数据库案例 from rest_framework.views import APIView from rest_framework.response import Response from django.db import transaction # 导入事务 class Creat(APIView): @transaction.atomic # 装饰事务 def post(self,request): ...... sid=transaction.savepoint() # 对下面代码建立事务 for product in all_product: product.product_id=str(product.product_id) order_data['order_total']+=product.price*buy_list[product.product_id] order_data['quantity']+=buy_list[product.product_id] #建立子订单 for i in range(3): stock=product.stock.quantity # 商品在库中的数量 new_stock=stock-buy_list[product.product_id] # 减掉用户购买数量 if new_stock<0: # 库存数小于用户购买数量 transaction.savepoint_rollback(sid) # 回滚 return Response({"code":203,"msg":f"{product.name}库存不足"}) # 使用乐观锁: quantity=字段, 在执行update前,锁定该字段的是否没有被改动,若改动了,则update影响调试为0 res=models.Stock.objects.filter(quantity=stock,stock_id=product.stock.stock_id).update(quantity=new_stock) if not res: if i==2: transaction.savepoint_rollback(sid) # 回滚 return Response({"code": 203, "msg": f"建立订单失败"}) else: continue else: break
悲观锁分为共享锁和排它锁。
共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据能够共享一把锁,共享锁是用来读取数据的。另外,一个事务获取了同一数据的共享锁,其余事务就不能获取该数据的排它锁。
排它锁又称为写锁,简称X锁,顾名思义,排它锁就是不能与其余所并存,如一个事务获取了一个数据行的排它锁,其余事务就不能再获取该行的其它锁,包括共享锁和排它锁。另外不存什么事务隔离级别,update/insert/delete会自动获取排它锁
共享锁获取方式:select * from account where name='jack' lock in share mode;
排它锁获取方式:select * from account where name='jack' for update;
MySQL分为表级锁和行级锁,共享锁和排它锁是行级锁。表级锁在此不作讨论。