深入理解mysql锁与事务隔离级别
视频讲解如下:
一、事务
1、事务四要素:ACID
对于事务,我之前的理解是很粗糙的,不就是为了保证操作的原子性么?一般订单系统或者财务系统跟钱打交道的都会用到对吧?事务可以保证所有的操作一次性提交,要么全部成功,要么全部失败,譬如 A 转账给 B 100 元,先从 A 的账户里扣除 100 元,再在 B 的账户上加上 100 元,这两个操作必须同时成功,如果 A 的钱减了之后程序异常了,事务会将 A 的钱回滚回去,就是这么简单啊。
确实如此,原子性可以说是事务四要素中最直白的一个,也是最容易理解的一个。但是我们往往忽略了事务的另外三个要素:一致性、隔离性和持久性。这四个要素合起来就是ACID。
原子性(Atomicity):要么全部完成,要么全部不完成;
一致性(Consistency):一个事务单元需要提交之后才会被其他事务可见;
隔离性(Isolation):并发事务之间不会互相影响,设立了不同程度的隔离级别,通过适度的破坏一致性,得以提高性能;
持久性(Durability):事务提交后即持久化到磁盘不会丢失。
关于一致性的概念比较模糊,网上也存在着几种不同的解释,譬如有的人理解是:事务开始前和结束后数据都是满足约束规则的,如外键约束。
但是对于我们了解死锁来说,我们还是应该把注意力放在 “I” 上面,因为不同的隔离级别,可能对应的加锁过程也不一样,而正是由于引入了各种各样的隔离级别,才让锁问题变得格外复杂。解决和分析死锁问题,首先得知道当前数据库的隔离级别。那么隔离级别是个什么东西呢?其实从上面也看的出来,隔离性和一致性是有冲突的,有时候为了提高性能,会适度的破坏一致性,可能会出现一个事务还没提交,它的修改就被其他事务看见了,这其实就是传说中的脏读,它属于最低级的隔离级别:读未提交。
2、事务并发存在的问题
在细讲事务的隔离级别之前,让我们先来看一下下面这张表,以及几个对这个表的操作,瞧瞧在多事务并发处理数据的时候可能会发生哪些意想不到的情况。
表很简单,就三个字段,主键 id、姓名 name 和余额 balance,其中 name 为二级索引,如下:
二.1 脏读(dirty read)
假设有两个事务,一个在进行转账操作,将 A 账户的 100 元转到 B 账户,同时有另一个事务在对 A 和 B 的账户余额进行求和统计,如下图所示:
我们不考虑任何加锁机制,仅仅从程序运行的角度来看,事务 1 执行成功之后,A 成功转了 100 元到 B 账户,这时 A 余额还剩 900 元,B 余额剩 1100,总和为 2000;但是事务 2 的统计求和算出来的结果却是 A + B = 1900。这个就是上面说的,没有提交的事务被其他事务读取到了,这叫做脏读。
2.2 不可重复读(unrepeatable read)
第二种场景叫不可重复读,如下图所示,事务 2 第一次获取 A 的账户余额为 1000 元,这个时候事务 1 对 A 的账户余额进行操作减去了 100 元,事务 2 再去查询 A 的账户余额发现变成了 900 元,这样就出现了同一个事务对同一条记录读取两遍,两次读出来的结果竟然不一样。
三、隔离级别
上面说了这么多并发场景下数据操作可能遇到的问题,那么要如何解决呢?我们能想到的最简单的方法就是对操作的数据进行加锁,写的时候不允许其他事务读,读的时候不允许其他事务写,这样是不是就完美解决了?确实如此。这其实就是四大隔离级别里的序列化,在序列化隔离级别下,可以保证事务的安全执行,数据库的一致性得以保障,但是它大大降低了事务的并发能力,性能最低。
为了调和事务的安全性和性能之间的冲突,适当的降低隔离级别,可以有效的提高数据库的并发性能。于是便有了四种不同的隔离级别:
读未提交(Read Uncommitted):可以读取未提交的记录,会出现脏读,幻读,不可重复读,所有并发问题都可能遇到;
读已提交(Read Committed):事务中只能看到已提交的修改,不会出现脏读现象,但是会出现幻读,不可重复读;(大多数数据库的默认隔离级别都是 RC,但是 MySQL InnoDb 默认是 RR)
可重复读(Repeatable Read):MySQL InnoDb 默认的隔离级别,解决了不可重复读问题,但是任然存在幻读问题;(MySQL 的实现有差异,后面介绍)
序列化(Serializable):最高隔离级别,啥并发问题都没有。
针对这四种隔离级别,应该根据具体的业务来取舍,如果某个系统的业务里根本就不会出现重复读的场景,完全可以将数据库的隔离级别设置为 RC,这样可以最大程度的提高数据库的并发性。不同的隔离级别和可能发生的并发现象如下表:
其中,在可重复读(RR)隔离级别下,是否可能出现第二类丢失更新问题(提交覆盖)比较有争议,有的人认为提交覆盖问题其实是不可重复读问题的一种特殊形式,所以在 RR 隔离级别解决了不可重复读,自然也不可能出现提交覆盖,但是也有人认为 RR 下会出现提交覆盖问题,比如 Mysql Repeatable-Read级别第二类丢失更新。
喜欢就给点关注吧!!
转自:http://www.aneasystone.com/archives/2017/10/solving-dead-locks-one.html
领取专属 10元无门槛券
私享最新 技术干货