1. 未提交读(READ_UNCOMMITED)解决丢失修改
[ 丢失修改:多个事务同时盯上了一个数据,然后各写各的,谁把谁覆盖了都不知道,总之谁写的快谁就会被覆盖丢失信息。]
为了解决丢失修改的写覆盖问题,未提交读规定:
1.事务A对当前被读取的数据不加锁
2.事务A开始更新一行数据时,必须先对其加共享锁,直到事务结束才释放
从第二点就可以看出,事务A在写入数据的时候加了共享锁,其他事务只能读,不能写,所以事务A在写的过程中也就不会被覆盖,从而就解决了丢失修改的问题
然而根据未提交读的原理, 其会引入新的问题:脏读
2. 提交读(READ_COMMITED)解决脏读
[ 脏读:指别的事务修改后还没提交数据,事务A就可以读到这些数据,比如银行不小心转了一个亿到你的账户,银行人员发现幸好还没提交,此时银行人员马上回滚撤销操作,但你却在其之前取读发现自己账户居然有一个亿,这就是脏读 ]
为了解决未提交读下的脏读问题,提交读规定:
1.事务A对当前被读取的数据加共享锁,一旦读完该行,立即释放该共享锁(注意是读完立即释放)
2.事务A在更新某行数据时,必须对其加上排他锁,直到事务结束才释放(注意是事务结束才释放)
从第二点就可以看出,事务A在写入数据时加了排他锁,意味着其他事务根本就不能读到A更新但未提交的数据,如果把A换成银行,其他事务换成你,那么在上面的例子中你就不可能读到银行转了但未提交的一个亿,所以也就避免了脏读。因为排它锁从根本上使得事务只能读取到已提交的数据。
然而根据提交读的原理,其会引入新的问题:不可重复读
3. 可重复读(REPEATABLE READ)解决不可重复读
[ 不可重复读:两次读之间有别的事务修改,导致读的结果不一致,比如你两次快速读支苻宝之间你的花呗迅速还款并提交了数据,这样你会发现你第二次读突然少了一笔钱,这就是不可重复读。当然有些时候不可重复读根本不是个问题,比如你会觉得少一笔钱很正常,猜得到是花呗还了。但有些时候不行,举个例子,A和B跑马拉松,本来都是100KM,但在A和B同时读取跑步距离的一瞬间,赛委会调整了比赛距离为20KM并提交了数据,此时一个人就知道跑20KM,另一个则会继续跑100KM ]
为了解决不可重复读,可重复读规定:
1.事务A在读取某数据时,必须先对其加共享锁,直到事务结束才释放(注意是事务结束才释放)
2.事务A在更新某数据时,必须先对其加排他锁,直到事务结束才释放(注意是事务结束才释放)
从第一点就可以看出,事务A在加的是共享锁,并且要到事务结束才会释放该锁,也就意味着A在两次读取数据期间,其他事务不能对该数据进行更改,也就不会出现上面跑马拉松时对比赛距离的修改,从而解决了不可重复读
然而根据可重复读的原理,其会引入新的问题:幻读
为了解决幻读,MySQL后来又在此基础上引入了MVCC,使用快照读和当前读来避免幻读
4. 串行化(SERIALIZABLE)解决幻读
[ 幻读:两次读之间有别的事务增删,比如事务A想统计年薪100W以上的有多少人,当A两次读数据之间有其他事务新添加了一个CTO的记录,他的年薪也是100W+,所以A第二次读取到的数据突然多了一个,仿佛出现了幻觉一般,这就是一种幻读 ]
为了解决幻读,串行化规定:
1.事务在读取数据时,必须先对其加表级共享锁(注意这里是表级) ,直到事务结束才释放;
2.事务在更新数据时,必须先对其加表级排他锁(注意这里是表级) ,直到事务结束才释放。
从表级锁就可以看出,通过在一次操作中对整张表进行加锁,从而其他事务对整张表既不能insert,也不能delete,所以不会有行记录的增加或减少,从而保证了当前事务两次读之间数据的一致性,解决了幻读问题
然而根串行化的原理,其会导致写冲突,因此并发度急剧下降,一般不推荐使用该隔离级别