目录
一、前言
在上篇事务一文中,我们提到了事务的四个属性 原子性、持久性、隔离性、一致性。原子性和持久性我们已经在上篇文章中见识到了,但是隔离性和一致性又该怎么理解呢?
我们首先来看事务的隔离性,数据库中为了保证事务执行过程中尽量不受干扰,就有了一个重要的特征:隔离性。
同时,数据库中允许事务受不同程度的干扰,就有了一种重要特征:隔离级别。
二、隔离级别
首先我们先了解一下MySQL数据库中定义了四种标准的隔离级别:
- 读未提交【Read Uncommitted】: 在该隔离级别,所有的事务都可以看到其他事务没有提交的执行结果。(实际生产中不可能使用这种隔离级别的),但是相当于没有任何隔离性,也会有很多并发问题,如脏读,幻读,不可重复读等,我们上篇文章为了做实验方便观察现象,用的就是这个隔离性。
- 读提交【Read Committed】 :该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默认的)。它满足了隔离的简单定义:一个事务只能看到其他的已经提交的事务所做的改变。这种隔离级别会引起不可重复读,即一个事务执行时,如果多次 select, 可能得到不同的结果。
- 可重复读【Repeatable Read】: 这是 MySQL 默认的隔离级别,它确保同一个事务,在执行中,多次读取操作数据时,会看到同样的数据行。但是会有幻读问题。
- 串行化【Serializable】: 这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题。它在每个读的数据行上面加上共享锁,。但是可能会导致超时和锁竞争(这种隔离级别太极端,实际生产基本不使用)
隔离级别如何实现:隔离,基本都是通过锁实现的,不同的隔离级别,锁的使用是不同的。常见有,表锁,行锁,读锁,写锁,间隙锁(GAP),Next-Key锁(GAP+行锁)等。不过,我们先了解就行。
1、查看与设置隔离性
设置当前会话 or 全局隔离级别语法
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ
COMMITTED | REPEATABLE READ | SERIALIZABLE}
如我们所想,设置当前会话隔离性,另起一个会话并不会影响新会话的隔离性。若设置全局隔离性,当另起一个新会话时,该会话的隔离性会被影响。
2、读未提交 【Read Uncommitted】
此时客户端A B都在事务中,都没有提交,可以看到在客户端隔离性为读未提交的状态下,客户端A在一个事务中对表中数据更新但是还没提交的情况下,客户端B在事务中也会看到表的更新。
上面的现象就称为脏读,即一个事务在执行中,读到另一个执行中事务的更新(或其他操作),但是未commit的数据。
该隔离性几乎没有枷锁,虽然效率高,但是问题太多了,所以严重不建议采纳。
3、读提交【Read Committed】
可以看到客户端B是在客户端A的事务提交之后才在它的事务中看到客户端A对表的修改的,而在客户端A没有提交的情况下,客户端B是看不到的,无论自己是否提交。
所以这就造成了在同一个事务内(客户端B事务),同样的读取,在不同的时间段(是在整个事务还在运行的时间段)读取到了不同的值,这种现象就称为 不可重复读
4、 可重复读【Repeatable Read】
可以看到只有在客户端AB同时提交事务之后,客户端B才能看到客户端A在事务中对于表中数据的更新。也就是说客户端B在自己的事务中无论读多少次,读到的內容都是一样的,这也就是为什么该隔离性被称为 可重复读。
这里如果将客户端A对表的更新操作变为插入操作,那么在客户端B的事务中还会有可重复读的情况吗?
我们要知道一般的数据库在可重复读的情况的时候,无法屏蔽其他事务insert的数据,因为隔离性实现是对数据加锁完成的,而insert待插入的数据因为并不存在,那么一般加锁无法屏蔽这类问题。会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的记录,就如同产生了幻觉。这种现象,叫做幻读(phantom read)。
但是MySQL在可重复读的情况下并没有出现幻读的问题,解决的方式是用Next-Key锁(GAP+行锁),这里稍作了解。
5、串行化 【Serializable】
此时客户端AB都在事务中,可以看到双方读取內容很正常,不会串行化,但是客户端A更新内容时, 会卡顿
当我们提交客户端B的事务后,客户端A的更新就可以继续,只不过由于事务等待的时间超过了MySQL的锁等待超时限制,所以出现了报错。
在RR隔离性下,除了update操作, insert,delete之间是会有加锁现象的,但是select和这些操作是不冲突的,这就是通过读写锁+MVCC完成的隔离性。
RR对所有操作全部加锁,进行串行化,不会有问题,但是只要串行化,效率很低,几乎完全不会被采用
三、总结
- 其中隔离级别越严格,安全性越高,但数据库的并发性能也就越低,往往需要在两者之间找一个衡点。
- 不可重复读的重点是修改和删除:同样的条件, 你读取过的数据,再次读取出来发现值不一样了幻读的重点在于新增:同样的条件, 第1次和第2次读出来的记录数不一样
- 说明: mysql 默认的隔离级别是可重复读,一般情况下不要修改
- 上面的例子可以看出,事务也有长短事务这样的概念。事务间互相影响,指的是事务在并行执行的时候,即都没有commit的时候,影响会比较大
接下来谈谈一致性
- 成功提交的结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中断,而改未完成的事务对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确(不一致)的状态。因此一致性是通过原子性来保证的。
- 其实一致性和用户的业务逻辑强相关,一般MySQL提供技术支持,但是一致性还是要用户业务逻辑做支撑,也就是,一致性,是由用户决定的。
- 事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务
感谢阅读!