数据库事务有不同的隔离级别,不同的隔离级别对锁的使用是不同的,锁的应用最终导致不同事务的隔离级别。MVCC(Multi-Version Concurrency Control)多版本并发控制,MVCC就是行级锁的一个升级。
事务的隔离级别是通过锁的机制来实现的,只不过隐藏了加锁细节。
表锁中读写是阻塞的,基于提高并发性能的考虑,MVCC一般读写是不阻塞的,所以MVCC很多情况下避免了加锁的操作。
MVCC实现的读写不阻塞:多版本并发控制—》通过一定机制生成一个数据请求时间点的一致性数据快照(snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取。从用户角度来看,好像数据库可以提供同一个数据的多个版本。
快照有两个级别(Repeatable read 和Read committed隔离级别都是为了解决读写冲突问题。):
- 语句级别:针对Read committed隔离级别
- 事务级别:针对Repeatable read隔离级别
事务的隔离级别:
1、Read uncommitted:会出现脏读,不可重复读,幻读。
脏读:一个事务读取到另一个事务未提交的数据。例如A给B转账,A执行了转账语句,但A还没有提交事务,B读取数据,发现账号钱边多了。B跟A说,已经收到钱了,A回滚事务rollback,等B再查找账号钱的时候发现钱没有增多。
出现脏读的原因是因为自己在读的时候没有加读锁,导致读取出还没释放锁的记录。
Read uncommitted过程:
(1) 事务A读取记录(没有加任何锁)
(2) 事务B修改记录(此时加了写锁,并且还没有commit,也就是没有释放写锁)
(3) 事务A再次读取记录(因为事务A在读取时没有加任何锁,所以可以读取到事务B还没有提交的(没释放掉写锁)的记录
2、Read committed:会出现不可重复读,幻读。
避免脏读的做法:在读取的时候生成一个版本号,直到事务其他commit被修改了一行才会有新的版本号。
Read committed的过程:
(1) 事务A读取了记录(生成版本号)
(2) 事务B修改了记录(此时也加了锁)
(3) 事务A再读取的时候按照最新的版本号来读取(当事务B执行commit一行,会生成一个新的版本号),如果事务B还没有commit,那事务A读取的还是之前版本号的数据。
Read committed出现的现象,不可重复读:一个事务读取到另一个事务已经提交的数据,意思是一个事务可以看到其他事务做的修改。
注意:A查询数据库得到数据,B去修改数据库的数据,导致A多次查询数据库的结果都不一样。危害:A每次查询的结果都是受B的影响的,那么A查询出来的信息也没意思了。Read committed是语句级别的快照,每次读取的都是当前最新的版本。
3、Repeatable read:会出现幻读(MySQL实现的Repeatable read配合gap锁不会出现幻读。)
Repeatable read避免不可重复读是事务级别的快照。每次读取的都是当前事务版本,即使被修改了,也只会读取当前事务版本的数据。幻读:在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
MySQL的Repeatable read隔离级别加上GAP间隙锁已经处理了幻读。
4、Serializable:串行,避免以上的情况。