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中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现
悲观锁中又分为共享锁和排他锁
-共享锁(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

浙公网安备 33010602011771号