各种非正常读
脏读
事务A读取到了事务B还未提交的数据,被称之为脏读。
不可重复读
在并发更新操作时,在同一次事务中,前后两次查询到的数据是不一样的。
幻读
在并发新增、修改时,在同一次事务中,前后两次查询到的数据量不一致。用户产生不可预料的问题
事务隔离级别
可以解决脏读、幻读、不可重复读的问题。
隔离级别 | 隔离级别英文 | 脏读 | 幻读 | 不可重复读 | 加锁读 | 说明 |
---|---|---|---|---|---|---|
读未提交 | Read UnCommitted (RU) | 是 | 是 | 是 | 否 | 能够读取到其它事物未提交的内容 |
读已提交 | Read Committed (RC) | 否 | 是 | 是 | 否 | 只能读取到其它事物提交成功的内容 |
可重复读 | Repeatable Read (RR) | 否 | 否 innoDB除外 | 是 | 否 | 读取某行后,不允许其它事物操作此行,直到事物结束 |
序列化 | Serializable | 否 | 否 | 否 | 是 | 等待其它事物执行完成后,依次执行,性能低,安全性高 |
MVCC并发版本控制
MVCC全称(Multiversion Concurrency Control)
在MySQL的InnoDB引擎下,RC、RR基于MVCC(多版本并发控制)进行并发事务控制,MVCC是基于数据版本来进行并发事务访问的一种技术。
RR在MVCC下不能保证不产生幻读的问题。因为如果在两次快照读中间穿插了当前读,那么ReadView会被重新生成,因此可能会导致幻读的问题产生。既第一次读到了3条数据,第二次读到了2条数据。
UNDO_LOG
UNDO_LOG(回滚日志)版本链条
MySQL会在确定UNDO_LOG没有其它事务引用时才会删除。
ReadView
ReadView就是快照读SQL执行时,MVCC提取数据的依据。当前读不会使用MVCC。
- 快照读:就是最基础的查询语句。使用MVCC。
- 当前读:执行下列语句时读取数据的方式。使用行锁 + 间隙锁。
- INSERT、UPDATE、DELETE
- SELECT ... FOR UPDATE
- SELECT ... LOCL IN SHARE MODE
ReadView属性
- m_ids:当前活跃的事务id列表
- 活跃:没有commit、rollback的事务
- min_trx_id:最小活跃的事务id
- max_trx_id:预分配的事务id,最大事务编号 +1
- cerator_trx_id:ReadView创建者的事务编号
ReadView生成时机:
- 读已提交RC:每次读取都会生成新的ReadView
- 可重复读RR:会复用第一次生成的ReadView
- 因为所使用的ReadView是一模一样的,所以RR是可重复读的。
ReadView提取数据的逻辑
ReadView会根据自己的属性,去到UNDO_LOG中提取数据,提取的顺序由UNDO_LOG的事务号从大到小依次读取,如果当前不满足,就会往下接着找,最终找到符合条件的数据。规则如下。
- 判断当前事务id是否等于,ReadView创建者的事务id,如果是的话就说明这个事务是自己的,可以访问数据。
- 判断trx_id < min_trx_id,自己的事务id是否小于最小的活跃事务id,如果是的话说明自己已经被提交了,可以访问。
- 判断 trx_id > max_trx_id 如果自己的事务id大于ReadView生成时的最大事务ID,那么说明自己还没有提交。不可以访问。
- 判断 min_trx_id <= trx_id <= max_trx_id,如果当前事务id大于等于最小事务id,并且小于等于预分配事务id,成立的话就去活跃的事务id列表中查找,没找到,说明自己被提交了,那就可以访问,否则就不可以访问。
参考地址:IT老齐
Spring事务
三方操作如何保证原子性
在开发业务的过程中,往往可能遇到这种场景,在数据库事务操作完毕后,给第三方组件、服务发送一条消息、HTTP请求。像这种操作如果直接写在事务内部,则有可能会影响主要业务代码的运行,如果写在外部,则无法保证事务操作与三方操作的原子性。
在Spring中提供了一个功能,事务回调接口