MySQL下事务隔离和锁的简单理解

1.ACID

在一个完美的事务中,会具有ACID特性,分别为原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability) 。

  • 原子性:满足原子操作单元,对数据的操作,要么全部执行,要么全部不执行。
  • 一致性:事务开始和完成时,数据都必须保持一致。
  • 隔离性:事务之间相互独立,中间状态对外部不可见。
  • 持久性:数据的修改是永久性的,即使系统出现任何故障都能够保持。

事务为多个操作的一个集合,为程序的一个执行单元。事务本身不包括ACID四个特性,只是开发者尽量为事务满足这四个特性,两者之间没有什么直接的关系。

2.为什么数据库需要有锁的机制

​ 在并发的情况下,多个事务或操作单元同时执行,难免会遇到如下的几个常见问题:

  • 脏读:存在两个事务A和B,事务A还没commit的时候,事务B就读到了A操作过的数据。没有commit的数据,不一定最终存在数据库,读到了这样的数据就是脏读。
  • 不可重复读:存在事务A,对数据进行多次读取,但期间事务B对该数据更新并提交,出现后面读取数据不一致或消失的情况。(重点在于更新和删除)
  • 幻读:存在事务A,读取数据发现不存在,再读取时发现又有数据的数据不一致情况。(重点在于插入)

为了有效保证并发时读取数据的正确性,在标准SQL规范中定义了四种事务隔离级别

3.事务隔离级别

在四个隔离级别中,每个级别的隔离程度不同,而产生的副作用也不同

读未提交(read-uncommitted):最低程度的隔离级别,只能保证数据的持久性和不允许更新丢失。

理解: 允许脏读,但不允许更新丢失。

读提交(read-committed):语句级别。不允许脏读但允许不可重复读和幻读。

理解:读事务允许其他事务对该数据读操作(行共享锁,读完立即释放),但写事务提交前会禁止其他事务访问该行数据。这样避免了脏读问题,但可能会引发不可重复读问题,如事务A在事务B之前读了该行数据,接着B更新后提交,事务A再读则数据发生不一致问题。

可重复读(repeatable-read):事务级别。只会引起幻读问题。

理解:事务A在读取同一数据,在事务没有结束时,其他事务不能对该数据进行操作。这样保证了两次读同一数据的一致性,所以该隔离级别成为可重复读。

串行化(serializable):事务序列化执行,毫无并发可言。

下列为每个隔离级别会产生的副作用表:

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)
读提交(read-committed) -
可重复读(repeatable-read) - -
串行化(serializable) - - -

4.锁的实现

这里先引入两个概念:

悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronizedReentrantLock等独占锁就是悲观锁思想的实现

悲观锁中又分为共享锁和排他锁

-共享锁(shared lock)又称s锁。共享锁在上锁时,多个事务都可读,但是不能写。

-排他锁(eXultation)又称x锁。一个事务A在获得了该数据的排他锁,那么其他事务将无法获取改行的锁,包括共享锁和排他锁。事务A则可以对改行数据读写操作。

乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

乐观锁有两种实现方式

1.CAS算法实现概念

当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS是一种非阻塞式的同步方式。

2.版本号控制实现概念

在表中加入一个version字段,记录数据更新的次数。当事务要修改该数据时,会获取到当前的version值,提交更新时,条件判断当前该记录的version跟事务获取到的version的值一样时,才会提交更新,否则会一直重复,直到更新结束。

版本号控制时CAS的升级实现,因为CAS算法会出现一个著名的ABA问题。

5.锁在InnoDB下的实现

  • 锁机制:阻止其他事务对数据进行操作, 各个隔离级别主要体现在读取数据时加的锁的释放时机。

    • 读未提交:事务读取时不加锁
    • 读提交:事务读取时加行级共享锁(读到才加锁),一旦读完,立刻释放(并不是事务结束)。
    • 可重复度:事务读取时加行级共享锁,直到事务结束时,才会释放。
    • 串行化:事务读取时加表级共享锁,直到事务结束时,才会释放。

    其他还有一些细节不同,主要就是这些

  • MVCC机制:生成一个数据快照,并用这个快照来提供一定级别的一致性读取,也称为多版本数据控制。

锁的算法

  • Record Lock: 单个行记录上的锁
  • Gap Lock :间隙锁,锁定一个范围,但不包含本身
  • Next-Key Lock : Gap Lock+ Recork Lock,锁定一个范围,并且锁定记录本身

Record Lock

Record Lock总是会锁住索引记录,如果InnoDB存储引擎在建立标的时候没有设置索引,这个时候会用隐式的主键来锁定.

Gap Lock

Gap Lock锁主要是用来锁住间隙的.也就是为了阻止多个事务将记录插入到同一范围内,这会导致幻读问题的产生.

Next-Key Lock

Next-Key Lock是结合了Gap Lock 和Record Lock的一种锁定算法.这种算法锁定的并不是单个值,而是一个范围.这个锁的出现也是为了防止幻读的出现.

参考文章: https://zhuanlan.zhihu.com/p/40211594

https://www.bilibili.com/video/BV16J411m7qi?from=search&seid=8830852944891447267

https://blog.csdn.net/weixin_42467553/article/details/91370186

posted @ 2020-09-29 20:23  树樁  阅读(241)  评论(1)    收藏  举报