1.事务的四大特性
1.原子性(Atomicity)
事务是一个不可分割的单位,事务中的操作要么全部成功,要么全部失败。
2.一致性(Consistency)
事务必须使数据库从一个一致性状态转变到另一个一致性状态;举个例子:A和B的总额为2000,无论他们之间如何转账,他们的总金额必须不变,这就是一致性。
3.隔离性(isolation)
多个用户并发访问数据库时,数据库为每个用户开启事务,不会被其他事务操作所影响,多个并发事务操作互相隔离。这个隔离性有四种级别。
4.持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
2.并发事务可能出现的问题
2.1.脏读
事务A读取了事务B更新的数据,然后事务B回滚了,那么事务A读取的数据是脏数据,因为事务A读取了事务B未提交的数据。
2.2.不可重复读
事务A多次读取同一个数据,但是事务B在事务A的读取间隙更新并提交了数据,导致事务A读取结果不一致。
2.3.幻读
这里经常会和不可重复读弄混淆,首先明白一点幻读并不是说“一个事务内进行两次相同操作居然得到了不一样的结果” 直接从例子入手:
T1: select * from test where id=1;
T2: insert into test(id,name) values(1,“A”) ;
T1: insert into test(id,name) values(1,“A”) ;
T1:检索id=1的数据,发现没有,插入。
T2:干扰T1事务的进行,在T1事务插入数据之前,先插入数据提交。
T1:此时T1插入数据,会报主键冲突。
对于T1来说第一次的查询并不能支持插入操作,但实际上是可以的,T1这里就是发生了幻读,数据就像凭空出现的一样。
所以 mysql 的幻读并非什么读取两次返回结果集不同,而是事务在插入事先检测不存在的记录时,惊奇的发现这些数据已经存在了,之前的检测读获取到的数据如同鬼影一般。
这里要灵活的理解读取的意思,第一次select是读取,第二次的 insert 其实也属于隐式的读取,只不过是在 mysql 的机制中读取的,插入数据也是要先读取一下有没有主键冲突才能决定是否执行插入。
不可重复读侧重表达 读-读,幻读则是说 读-写,用写来证实读的是鬼影。
3.事务的隔离级别
3.1.读未提交(read uncommitted)
可以读取未提交的数据,会出现上面的脏读现象(此隔离级别不会使用,不做解释)
3.2.读已提交(read committed)
不能读取未提交的数据,这里就防止了脏读现象,但是避免不了不可重复读
时间 事务A 事务B
T1 开始事务
T2 开始事务
T3 查询余额1000
T4 查询余额1000
T5 取出1000,余额0
T6 提交
T7 查询余额0
T8 提交
从上面我们可以看出事务A什么都没做 只是查询了两次数据,余额就从1000变成0
3.3.可重复读(repeatable-read)(MySQL/InnoDB下的默认级别)
针对当前读,RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),不存在幻读现象。
3.4.串行化(serializable)
从MVCC并发控制退化为基于锁的并发控制。不区别快照读与当前读,所有的读操作均为当前读,读加读锁 (S锁),写加写锁 (X锁)。
Serializable隔离级别下,读写冲突,因此并发度急剧下降,在MySQL/InnoDB下不建议使用。
幻读:事务A执行了一次读操作,此时事务B在事务A的影响区间内插入了一条数据,此时事务A在执行一次读操作时,会发现出现了不合理的数据。
4.Gap Lock
间隙锁,是在索引的间隙之间加上锁,这是为什么Repeatable Read隔离级别下能防止幻读的主要原因。
5、间歇锁具体例子: