五、事务的隔离级别
MySQL服务可能同时被多个客户端进程(线程)访问,访问的方式以事务的方式进行
一个事务可能由多条SQL语句构成,也意味着任何一个事务,都有执行前、执行中和执行后三个阶段,而所谓的原子性就是让用户层要么看到执行前,要么看到执行后,执行中若出现问题,可以随时回滚,所以单个事务对用户表现出来的特性就是原子性
但毕竟每个事务都有一个执行的过程,在多个事务各自执行自己的多条SQL时,仍然可能会出现互相影响的情况,如多个事务同时访问同一张表,甚至是表中的同一条记录
数据库为了保证事务执行过程中尽量不受干扰,于是出现了隔离性的概念,而数据库为了允许事务在执行过程中受到不同程度的干扰,于是出现了隔离级别的概念
数据库事务的隔离级别:
读未提交(Read Uncommitted): 在该隔离级别下,所有的事务都可以看到其他事务没有提交的执行结果,实际生产中不可能使用这种隔离级别,因为这种隔离级别相当于没有任何隔离性,会存在很多并发问题,如脏读、幻读、不可重复读等
读提交(Read Committed): 该隔离级别是大多数数据库的默认隔离级别,但并不是MySQL默认的隔离级别,其满足了隔离的简单定义:一个事务只能看到其他已经提交的事务所做的改变,但这种隔离级别存在不可重复读和幻读的问题
可重复读(Repeatable Read): MySQL默认的隔离级别,该隔离级别确保同一个事务在执行过程中,多次读取操作数据时会看到同样的数据,即解决了不可重复读的问题,但这种隔离级别下仍然存在幻读的问题
串行化(Serializable): 事务的最高隔离级别,该隔离级别通过强制事务排序,使之不可能相互冲突,从而解决了幻读问题。在每个读的数据行上面加上共享锁,但是可能会导致超时和锁竞争问题,这种隔离级别太极端,实际生产中基本不使用
注意:
虽然数据库事务的隔离级别有以上四种,但一个稳态的数据库只会选择这其中的一种,作为默认隔离级别。但数据库默认的隔离级别有时可能并不满足上层的业务需求,因此数据库提供了这四种隔离级别,可以自行设置
隔离级别基本上都是通过加锁的方式实现的,不同的隔离级别对锁的使用是不同的,常见的有表锁、行锁、写锁、间隙锁(GAP)、Next-Key锁(GAP+行锁)等
5.1 查看与设置隔离级别
查看全局隔离级别
select @@global.tx_isolation
查看会话隔离级别
通过下面两种方式都可以查看当前会话下的隔离级别
select @@session.tx_isolation; select @@tx_isolation;
设置会话隔离级别
set session transaction isolation level 隔离级别
注意:设置会话隔离级别只影响当前会话,新起的会话依旧采用全局隔离级别
设置全局隔离级别
set global transaction isolation level 隔离级别
注意:设置全局隔离级别会影响后续的新会话,但当前会话隔离级别不会发生变化,若要让当前会话的隔离级别也改变,则需设置会话隔离级别或重启会话
5.2 读未提交(Read Uncommitted)
启动两个终端,将隔离级别都设置为读未提交,并查看此时银行用户表中的数据
在两个终端各自启动一个事务,左终端的事务所作的修改在没有提交之前,右终端中的事务就能够看到了
读未提交是事务的最低隔离级别,几乎没有加锁,虽然效率高,但是问题比较多,所以严重不建议使用
一个事务在执行中,读到另一个执行中事务的更新(或其他操作)但是未commit的数据,这种现象叫做脏读
5.3 读提交(Read Committed)
启动两个终端,将隔离级别都设置为读提交,并查看此时银行用户表中的数据
两个终端各自启动一个事务,左终端的事务所作的修改在没有提交之前,右终端的事务无法看到
只有左终端的事务提交后,右终端的事务才能看到修改后的数据
注意:一个事务在执行过程中,两个相同的select查询得到了不同的数据,这种现象被称为不可重复读
5.4 可重复读(Repeatable Read)
启动两个终端,将隔离级别都设置为可重复读,并查看此时银行用户表中的数据
在两个终端各自启动一个事务,左终端中的事务所作的修改在没有提交之前,右终端中的事务无法看到
并且当左终端中的事务提交后,右终端中的事务仍然看不到修改后的数据
只有当右终端的事务提交后再查看表中的数据,这时才能看到修改后的数据
在可重复读隔离级别下,一个事务在执行过程中,相同的select查询得到的是相同的数据,即可重复读
一般的数据库在可重复读隔离级别下,update数据是满足可重复读的,但insert数据会存在幻读问题,因为隔离性是通过对数据加锁完成的,而新插入的数据原本是不存在的,因此一般的加锁无法屏蔽这类问题
一个事务在执行过程中,相同的select查询得到了新的数据,如同出现了幻觉,这种现象被称为幻读
MySQL解决了可重复读隔离级别下的幻读问题【MySQL通过Next-Key锁(GAP+行锁)来解决幻读问题】,如重新在这两个终端各自启动一个事务,左终端中的事务向表中插入数据的在没有提交前,右终端中的事务无法看到
当左终端中的事务提交后,右终端中的事务仍然看不到新插入的数据
只有当右终端的事务提交后再查看表中的数据,此时才能看到新插入的数据
5.5 串行化
启动两个终端,将隔离级别都设置为串行化,并查看此时银行用户表中的数据
在两个终端各自启动一个事务,若两个事务都对表进行的是读操作,那么这两个事务可以并发执行,不会被阻塞
但若这两个事务中有一个事务要对表进行写操作,那么这个事务就会立即被阻塞
直到访问这张表的其他事务都提交后,这个被阻塞的事务才会被唤醒,然后才能对表进行修改操作
串行化是事务的最高隔离级别,多个事务同时进行读操作时加的是共享锁,因此可以并发执行读操作,但一旦需要进行写操作,就会进行串行化,效率低,几乎不会使用
5.6 隔离级别总结
MySQL中隔离级别:
隔离级别越严格,安全性越高,但数据库的并发性能也就越低,在选择隔离级别时需在两者之间找一个平衡点
表中只写出了各种隔离级别下进行读操作时是否需要加锁,因为无论哪种隔离级别,只要需进行写操作就一定要加锁