1. 事务隔离级别
MySQL是一个客户端/服务器架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接之后,就可以称之为一个会话。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事物的一部分,也就是对于服务器来说可能处理多个事务。事务有隔离性的特性,理论上在某个事物对某个数据进行访问时,其他事物应该进行等待排队,当该事务提交后,其他事务才可以继续访问这个数据。但这样对性能影响太大。我们既想要保持事务的隔离性,又想让服务器在处理访问同一个诗剧的多个事务的性能尽量高一些,那就看二者如何权衡取舍了。
2. 数据并发问题
针对事务的隔离性和并发性,我们会怎么做出取舍呢?先看一下访问相同数据的事务在不保证串行执行(也就是执行完一个再执行下一个)的情况可能会出现哪些问题。
1). 脏写(Dirty Write)
对于两个事务SessionA和Session B,如果SessionA修改了另一个尚未提交的事务Session B修改过的数据,那意味着发生了脏写。示意图:
2). 脏读(Dirty Read)
对于两个事务SessionA和Session B,SessionA读取了已经被SessionB更新但还没有提交的字段。之后若SessionB回滚,SessionA读取的内容就是临时且无效的。示意图:
3). 不可重复读(Non-Repeated Read)
对于两个事务SessionA和Session B,SessionA读取了一个字段,然后SessionB更新了该字段。之后SessionA再次读取了同一个字段,此时值就不同了。那就意味着发生了不可重复读。
我们在SessionB提交了几个隐式事务(注意是隐式事务,意味着语句结束后自动提交),这些事务都修改了studentno列为1的记录的列name的值,每次事务提交之后,如果SessionA中的事务都可以查看到最新的值,这种现象称为不可重复读。
4). 幻读(Phantom)
对于两个事务SessionA和Session B,SessionA从表中读取了一个字段,然后SessionB在该表中插入了一些新的行。之后,如果SessionA再次读取同一个表,就会多出几行。那么就意味着发生了幻读。插入的一些记录称为幻影记录。
删除一些记录的情况并不算幻读。
2. sql的四种隔离级别
上面介绍了几种并发事务执行过程中可能遇到的一些问题,这些问题又轻重缓急之分,我们会给这些问题按照严重情况排序:脏写>脏读>不可重复读>幻读。
我们愿意舍弃一部分隔离性来换取一部分性能在这里能体现在:设立一些隔离级别,隔离级别越低,并发问题发生的就越多。sql标准中设立了4个隔离级别。
- READ UNCOMMITTED:读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。不能避免脏读,不可重复读和幻读。
- READ COMMITTED:读已提交,它满足了隔离的简单定义:一个事务只能看见已经提交的事务所做的改变。这是大多数数据库系统的默认的隔离级别(但不是MySQL默认)。可以避免脏读,但不可重复读和幻读的问题仍然存在。
- REPEATABLE READ:可重复读。事务A在读到一条数据后,此时事务B对该数据进行了修改并提交,那么事务A再读取数据,读到的还是原来的内容。可以避免脏读,不可重复读,但幻读的问题仍然存在。这是MySQL的默认隔离级别。
- SERIALIZABLE:可串行化,确保事务可以从一个表中读取相同的行。再这个事务的持续期间,禁止其他事务对该表执行插入删除更新等操作。所有的并发问题都可以避免,但性能十分低下。能避免脏读,不可重复读和幻读。
SQL标准中规定,针对不同的隔离级别,并发事务可以发生不同程度的问题,具体情况如下:
脏写这个问题比较严重,不论哪个隔离级别,都不允许脏写的情况发生。
不同的隔离级别有不同的现象,并有不同的锁和并发机制。
3. MySQL支持的四种隔离机制
不同的数据库厂商对sql标准规定的四种隔离级别的支持不一样。比如Oracle就只支持READ COMMITTED和SERIALIZABLE两种隔离级别。MySQL虽然支持四种隔离级别,但与sql标准所规定的各个隔离级别允许发生的问题却有些出入。MySQL在REPEATABLE READ级别下,是可以禁止幻读问题的发生。
MySQL默认隔离级别是REPEATABLE READ,我们可以手动修改事务的隔离级别。
查看隔离级别:
# MySQL 5.7.20版本之前:
show variables like 'tx_isolation';
# 之后:
show variables like 'transaction_isolation';
# 不同版本都可以使用:
select @@transaction_isolation;
修改事务隔离级别:
set [global | session] transaction_isolation = '隔离级别';
# 其中隔离级别:
> READ-UNCOMMITTED
> READ-COMMITTED
> REPEATABLE-READ
>SERIALIZABLE