MySQL 事务隔离 | 乐观锁

MySQL 事务隔离级别详解

数据库事务具备四个特征,简称为事务的ACID特性mysql

原子性(Atomicity)、
一致性(Consistency)、
隔离性(Isoation)、
持久性(Durability),

什么是事务隔离

事务的隔离性是指在并发环境中,并发的事务是相互隔离的,
能够理解为多个事务同一时间段对数据库的增删改时是要隔离的

 隔离种类

SQL标准中定义了四种数据库事务隔离级别,级别从低到高分别为:sql

读未提交(Read Uncommitted)、-> 脏读
读已提交(Read Committed)、 -> 不可重复读
可重复读(Repeatable Read)、-> 换读
串行化(Serializable)。
在事务的并发操做中会出现脏读、不可重复读、幻读。在事务的并发操做中第二类更新丢失能够经过乐观锁和悲观锁解决。

 

 

读未提交(Read Uncommitted)

  • 该隔离级别,全部事务均可以看到其余未提交事务的执行结果。通俗地讲就是,在一个事务中能够读取到另外一个事务中新增或修改但未提交的数据。
  • 该隔离级别可能致使的问题是脏读。由于另外一个事务可能回滚,因此在第一个事务中读取到的数据极可能是无效的脏数据,形成脏读现象。
>: set tx_isolation='READ-UNCOMMITTED';

 

 eg:

脏读(读取未提交数据)

A事务读取B事务还没有提交的数据,此时若是B事务发生错误并执行回滚操做,那么A事务读取到的数据就是脏数据。就好像本来的数据比较干净、纯粹,此时因为B事务更改了它,这个数据变得再也不纯粹。这个时候A事务当即读取了这个脏数据,但事务B良心发现,又用回滚把数据恢复成原来干净、纯粹的样子,而事务A却什么都不知道,最终结果就是事务A读取了这次的脏数据,称为脏读。数据库

这种状况常发生于转帐与取款操做中
django

 

 

读已提交(Read Committed) 

  • 这是大多数数据库系统的默认隔离级别(但不是mysql默认的)
  • 一个事务只能看见已经提交事务所作的修改。换句话说,一个事务从开始直到提交以前,所作的任何修改对其余事务都是不可见的。
  • 该隔离级别可能致使的问题是不可重复读。由于两次执行一样的查询,可能会获得不同的结果。
>: set tx_isolation='READ-COMMITTED';

 

 eg:

不可重复读(先后屡次读取,数据内容不一致)

事务A在执行读取操做,由整个事务A比较大,先后读取同一条数据须要经历很长的时间 。而在事务A第一次读取数据,好比此时读取了小明的年龄为20岁,事务B执行更改操做,将小明的年龄更改成30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和以前的数据不同了,也就是数据不重复了,系统不能够读取到重复的数据,成为不可重复读。
并发

 

 

可重复读(Repeatable Read)

  • 这是MySQL的默认事务隔离级别
  • 它确保同一事务的多个实例在并发读取数据时,会看到一样的数据行。通俗来说,可重复读在一个事务里读取数据,怎么读都不会变,除非提交了该事务,再次进行读取。
  • 该隔离级别存在的问题是幻读
>: set tx_isolation='REPEATABLE-READ';

 

eg:

幻读(先后屡次读取,数据总量不一致)

事务A在执行读取操做,须要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操做并提交后,这个时候事务A读取的数据总量和以前统计的不同,就像产生了幻觉同样,无缘无故的多了几条数据,成为幻读。post

 

 

串行化(Serializable)

  • 这是最高的隔离级别
  • 它经过强制事务排序,使之不可能相互冲突,从而解决幻读问题。通俗地讲就是,假如两个事务都操做到同一数据行,那么这个数据行就会被锁定,只容许先读取/操做到数据行的事务优先操做,只有当事务提交了,数据行才会解锁,后一个事务才能成功操做这个数据行,不然只能一直等待
  • 该隔离级别可能致使大量的超时现象和锁竞争。
>: set tx_isolation='SERIALIZABLE';

 

不可重复读和幻读到底有什么区别呢?

(1)不可重复读是读取了其余事务更改的数据,针对insert与update操做atom

解决:使用行级锁,锁定该行,事务A屡次读取操做完成后才释放该锁,这个时候才容许其余事务更改刚才的数据。spa

(2)幻读是读取了其余事务新增的数据,针对insert与delete操做调试

解决:使用表级锁,锁定整张表,事务A屡次读取数据总量以后才释放该锁,这个时候才容许其余事务新增数据。

rest

乐观锁

    在更新语句中增长过滤条件,进行版本的判断,能够是这条记录的某个字段值。而后经过影响行数来判断是否更新成功。影响条数为0则更新失败,表明这条记录被其余事务修改了.

eg: 事务+乐观锁的使用

# 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分为表级锁和行级锁,共享锁和排它锁是行级锁。表级锁在此不作讨论。

相关文章
相关标签/搜索