事务的概念
事务是数据库的最小的操作单元,要么全部执行,要么都不执行。事务是MySQL中存储引擎方面支持的功能。MySQL支持多引擎,但是不是所有的引擎都支持事务,MyISAM不支持事务,InnoDB支持事务。
事务的特征
1、原子性
事务中的操作要么完全执行,要么都不执行
2、持久性
事务提交后的数据要能够持久保存,即使系统故障数据也不会丢失。
3、隔离性
并发事务之间需要有一定的隔离性,防止数据的不一致。
4、一致性
事务执行前后,数据要保持一致。这个一致性是用户定义的,比如转账系统中的一致性就是所有账户金额必须保持不变。
一致性依赖于原子性和隔离性。
原子性
事务的原子性,InnoDB是通过undolog回滚日志来实现的。对于需要回滚的事务,通过回滚日志进行回滚。具体原理见:第三篇:MySQL中InnoDB的undo日志的物理结构、事务回滚、MVCC
持久性
事务的持久性,通过redolog在实现持久化的同时,并提高了效率。具体原理见第四篇:MySQL之binlog和redolog
隔离性
我们这篇文章详细说一下隔离性。
隔离性中牵扯到了锁的一些知识,如果对MySQL中锁不太了解的话,建议看下我之前的文章第八篇:MySQL之锁详解
隔离性主要就是针对并发事务出现的问题。
并发事务有可能会出现以下问题。
1、脏读
事务B更改T的值后,事务A再次读取的时候能立即看到。当事务B回滚后,就会造成事务A之前读取的值为脏数据。
2、不可重复读
事务A在一个事务执行期间,发起了多次查询操作。事务B在此期间更改了数据,导致事务A在事务执行期间的数据不一致,也就是重复读取的前后数据不一致。
某些场景会要求在同一个事务执行期间数据保持不变。
3、幻读
事务A在事务执行期间多次执行当前读,但是事务B在事务A查询的间隙插入了新的数据,导致事务A查询前后的数据的数量不一致。
幻读和不可重复读的不同就是幻读针对的是前后查询的数据数量不一致
MySQL中有四种隔离级别。分别为
1、读未提交
2、读已提交
3、可重复读
4、串行化
从上到下,隔离级别越来越高,并发度越来越低。
1、读未提交
在读未提交下,
快照读: 直接读取最新的数据,什么也不做
当前读: 读取最新的数据,加上对应的行锁
读未提交下出现 脏读、不可重复读、幻读。
但是如果当前读-当前读或者当前读-快照读的话,不会造成脏读,因为对相关的记录上行锁了,其他事务无法修改。
如果是快照读-当前读的话,也不会造成脏读,因为第二次当前读压根就读不到数据,拿不到锁,会被阻塞。
如果是快照读-快照读的话,会造成脏读。
2、读已提交
快照读: 读已提交下,快照读会利用MVCC,具体的原理可以见具:第三篇:MySQL中InnoDB的undo日志的物理结构、事务回滚、MVCC
我们这里简单的说一下MVCC的原理:
MVCC会为事务构建一个readview,readview里面存放的是当前活跃的事务列表。
如果对应的数据行的事务ID大于readview中的最大事务ID,或者出现在readview中,就说明这条记录不可见。否则这条记录就是可见的。
如果记录不可见,会沿着undo日志链找寻之前的版本,做同样的检验,如果可见就返回历史版本。如果找到头,也没有找到可见的版本,就说明这条记录是先插入的,不可见。
读已提交的隔离级别下,事务中每次select的时候,都会重新生成新的readview。当其他事务如果提交后,其他事务就不是活跃的事务了,如果事务再次select,会重新生成readview,readview中就不含有其他事务的ID了,当前事务就对其他事务更新的记录可见了。
当前读: 对对应的行加行锁
读已提交 解决了 脏读的问题。当事务还未提交的时候,快照读读取的是历史版本的数据。
读已提交不能解决可重复读和幻读。
3、可重复读
快照读:通过undolog也就是回滚日志实现了MVCC(Multi-Version Concurrency Controll——多版本并发控制)。具体的原理可以见具体原理见:第三篇:MySQL中InnoDB的undo日志的物理结构、事务回滚、MVCC。
和读已提交不同,可重复读对应的readview在第一次select的时候生成,并且在事务的执行期间,readview一直不变。
当前读:除了对相应的记录加行锁外,还会加间隙锁,防止其他事务插入数据
可重复读解决了不可重读的问题,在事务执行期间,对应的readview不变,其他事务提交了数据,也是不可见的。
只有快照读-快照读、当前读-当前读、当前读-快照读不会出现幻读
而快照读-当前读还是会出现幻读的问题。
4、串行化
快照读:快照读也会加锁,并且对于范围查找和非唯一索引的定值查找会加间隙锁,和可重复读的当前读加锁方式完全一致
当前读:加锁方式和可重复读的当前读加锁方式一样。
串行化中,快照读也会加锁,包括间隙锁,而不是通过mvcc,解决了幻读问题。
总结:
MySQL的快照读 通过 MVCC的方式 保证 读写可以进行,进一步提高了并发。
而当前读通过加锁的方式,读写不能同时进行,并发度比较低了。
我们可以根据我们的业务场景来选择最合适的隔离级别。