事务分为三个主要知识点,分别是基本属性,并发事务处理带来的问题,以及事务的隔离级别。
一、事务的基本属性
1. 原子性(Atomicity)
事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全不执行
2. 一致性(Consistent)
在事务开始和完成时,数据都必须保持一致状态,这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性
3. 隔离型(Isolation)
数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的独立环境执行
4. 持久性(Durable)
事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持
二、 锁的分类
Shared and Exclusive Locks
这两个锁类似于Java中的读写锁,其中Shared Lock相当于Java中的读锁,读写、写写是互斥的,读读是可以并发的;Exclusive Lock相当于Java中的写锁,读写、写写、读读都是互斥的
Record Locks
行锁,顾名思义,是加在索引行(对!是索引行!不是数据行!)上的锁。比如select * from user where id=1 and id=10 for update,就会在id=1和id=10的索引行上加Record Lock
Gap Locks
间隙锁,它会锁住两个索引之间的区域(锁加在不存在的空闲空间)。比如select * from user where id>1 and id<10 for update,就会在id为(1,10)的索引区间上加Gap Lock
Next-Key Locks
也叫间隙锁,它是Record Lock + Gap Lock形成的一个闭区间锁。比如select * from user where id>=1 and id<=10 for update,就会在id为[1,10]的索引闭区间上加Next-Key Lock
三、CRUD操作的加锁类型
在数据库增删改查中,增删改都是加排他锁,也就是exclusive lock,而select只有显示声明才会加锁:
select:最常用的查询,不加锁
select...lock in share mode:加共享锁,也就是shared lock,其他事务可以读取这些行,但是不能修改他们直到事务提交
select...for update:加排他锁,也就是exclusive lock
四、并发事务处理的问题
并发事务处理主要有四种问题,更新丢失,脏读,不可重复读,幻读。
1. 更新丢失
概念:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题,即最后的更新覆盖了由其他事务所做的更新
常见场景:update
2. 脏读
概念:一个事务读取另一个事务未提交的数据:事务1读取一条数据并做了修改,此时事务2读取事务1已修改且未提交的数据;此时如果事务1回滚了修改操作,那么事务2读取的数据就是脏数据
常见场景:回滚
3. 不可重复读
概念:一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了。这种现象就叫做不可重复读。也就是说,事务A读取到了事务B已经提交的修改数据,不符合隔离性。
常见场景:update或delete
4. 幻读
概念:一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象被称为幻读。也就是说,事务A读取到了事务B提交的新增数据,不符合隔离性
常见场景:insert
五、 事务隔离级别及底层原理
事务隔离级别是为了避免上述并发事务处理所带来的问题的
不同事务隔离级别的底层是由MVCC版本生成时机,写锁释放时机,锁范围三者共同实现的
1. 读未提交,Read Uncommited。
事务之间可以读取彼此未提交的数据
原因:执行完写操作后立即释放锁,而不是在事务提交以后释放
后果:极易出现脏读
2. 读已提交,Read Committed。
这个隔离级别可以解决脏读的问题。
写锁在事务提交以后释放,读操作无视写锁,通过MVCC获取当前数据的最新快照,锁范围是行锁
后果:因为读取的是MVCC最新快照,因此无法避免其他事物对读取数据的更新,那就出现了不可重复读;因为没有间隙锁,所以其他事务是可以往区间中插入/删除数据的,就出现了幻读
3. 可重复读。Repeatable Read。
在这个隔离级别下,可以解决不可重复读的问题以及部分幻读问题。
写锁在事务提交以后释放,MVCC值在第一次select时生成版本,避免了不可重复读;如果锁的范围在行锁的基础上,加上Gap Lock,从而形成Next-Key Lock,阻塞其他事务在遍历范围内进行写操作,避免了幻读
可能导致幻读的情况:如果读取时不使用select...lock in share mode和select...for update,那么其他事务就可以在遍历范围内进行写操作
4. 串行化,Serialization。
串行化可以解决幻读的问题。
会将所有select转化为select...lock in share mode执行,即针对同一数据的所有读写都变成互斥的了,可靠性大大提高,并发性大大降低
Mysql的默认隔离级别为可重复读。
总结
数据的事务隔离级别分为4种,从低到高依次为读未提交,读已提交,可重复读,串行化。
可能导致的并发问题有脏读,不可重复读,幻读
通过将写锁持续到事务结束,可以避免事务互相读取的问题,为了提高并发性,引入MVCC获取每次select的值做到读写分离,提升至读已提交级别,解决了脏读问题;
通过MVCC只获取第一次select的值,可以避免不可重复读,设置可选的读加间隙锁,提升至可重复读级别
默认将读设置加间隙锁,解决了幻读问题,提升至串行化级别