关键词:一致性、隔离性、幻读、
前言:
假设此时事务A和事务B同时执行。
一、事务的定义&&特性:
1、定义:
对数据库进行的一组操作序列,同时这组操作序列必须满足ACID四个特性。
2、事务的特性:
①原子性(Atomic):指对数据库的操作要么全部一起执行,要么全部不执行;
②一致性(Consistency):指的是事务必须保证系统从某一个一致性状态转变为另外一个一致性状态。;
③隔离性(Isolation):并发事务执行过程中,不同的事务之间相互隔离执行的一个特性;
④持久性(Durability):事务提交后,对系统的影响是永久的。
详见:https://blog.csdn.net/chosen0ne/article/details/10036775
二、事务的一致性与隔离性的分析:
1、一致性:
(1)定义:
事务保证系统从某一个一致性状态转变为另外一个一致性状态。
(2)分析:
老梁向小梁转账,假设转账之前这大小梁的钱加起来总共是10000,那么老梁向小梁转账之后,不管这两个账户怎么转,老梁的钱和小梁的钱加起来的总额必须还是10000,这个就是事务的一致性。
2、隔离性:
(1)定义:
并发事务执行过程中,不同的事务之间相互隔离执行的一个特性。
(2)隔离级别:
①读未提交(Read Uncommitted):在事务A执行过程中,可以读取事务B未提交的数据;
②读提交(Read Committed):在事务A执行过程中,只能读取事务B提交的最新的内容;
③可重复读(Repeated Read):在事务A执行过程中,没办法读取事务B更新的数据(不管是提交还是未提交的),只有在事务A执行完毕的时候才能够读取到事务B的更新;
④串行化(Serialization):在事务A执行过程中,事务B没办法进行任何操作,只能等待事务A执行完再执行事务B。
事务的隔离强度:Read Uncommitted < Read Committed < Repeated Read < Serialization
详见:https://baijiahao.baidu.com/s?id=1629344395894429251&wfr=spider&for=pc
(3)并发事务执行过程中会产生的三个问题:
①脏读:指的是执行事务A时,读取事务B未提交的数据,但在这之后事务B进行了回滚操作,导致刚才事务A读取的数据时无效的。此时读取了“脏”的数据,所以称为脏读。
②不重复读:指的是执行事务A时,如果事务B不断commit,那么事务A每次都可以读到事务B提交的最新的数据。这就导致了事务A先后两次读到的数据结果会不一致,也就是说每次读的数据都是不重复的。所以称为不重复读。
③幻读:指的是执行事务A时,事务A只能够读取事务B第一次commit的内容;如果在事务A执行过程中,事务B又commit了修改,那么是不会影响到事务A。但是当事务A执行完,也就是在它commit的时候,就会发现原来数据竟然已经被事务B更改了。这样子就产生了幻读的现象。
(4)"隔离级别"与"产生的问题"的详细分析:
①不同隔离级别会产生的问题:
- Read Uncommitted级别:脏读、不重复读、幻读
- Read Committed级别:不重复读、幻读
- Repeated Read级别:幻读
- Serialization级别:都不会产生
②Repeatable Read级别 && 幻读:
数据库的四种隔离级别在不同数据库中的实现是不同的。在mysql的innodb引擎中,在实现Repeatable Read这个隔离级别的时候,已经解决了幻读的问题了。(虽然SQL标准中没有要求解决幻读,但是innodb中通过使用MVCC+间隙锁解决了幻读问题。)(幻读问题解决参见:MVCC--多版本并发控制机制中的第7点的分析)
(5)不同隔离级别下的读操作和写操作下的读和写操作的区别:
前提:重点分析Repeatable Read级别和Serialization级别;innodb引擎。
a、Repeatable Read级别:
- 事务A写:会加X锁,此时事务B肯定不能进行任何操作;
- 事务A读:对于快照读,通过MVCC来控制增删改查等操作;对于当前读,除了对当前记录加锁之外还需要结合gap锁来对记录的间隙加锁以保证了事务B只能读取数据而不能进行其他写操作。
b、Serialization级别:
- 不管是读还是写,都是串行化的。事务A在操作的时候,事务B只能等待。
(6)如何实现Read Commit隔离级别下的效果:
①效果:当前事务只能读其他事务已提交的记录:
②分析:
InnoDB 支持行锁,进行写操作时加的是行级中的排他锁,那么当其他事务访问另一个正在update (除select操作外其他操作本质上都是写操作)同一条记录的事务时,事务的读操作会被阻塞。所以只能等到记录(其实是索引上的锁)上的排他锁释放后才能进行访问,也就是另一个事务提交之后才能够访问。这样确实就实现read commited隔离级别的效果。
参考: