事务隔离级别/MVCC版本控制

226 阅读3分钟

事务隔离级别

sql标准中定义了事务的四种隔离级别:读未提交/读已提交/读可重复/可序列化,这四种隔离级别是循序渐进,越来越严格的。

读未提交

读到(另一个事务)未提交(的数据),出现脏读。postgre无此级别。

读已提交

只能读(另一个事务)已提交(的数据),该级别是postgre的默认级别。

读已提交存在的问题:不可重复读(避免不可重复读需要锁行)。即使数据的读取都在事务提交后,假设事务A需要读取num,第一次读取num=1,在事务A开始后,事务B立即对num进行更改提交后num=2,事务A再次读取num时,就发现和第一次的数据不一致了。pg中这一级别称之为Read Committed Snapshot读已提交快照。

读可重复

可以(在事务内)重复读取到(相同的数据)

读可重复解决了数据库不可重复读的问题。在一个事务开启后,无论其他事务再怎么对数据进行修改,也保证读取到的数据与之前一致。读可重复的实现:MVCC多版本并发控制/加行排他锁。对行加排他锁可以防止其他事务对该行进行修改,但是此时还是可以插入数据的,所以会产生一个事务内第一次查询的结果数量小于第二次查询结果数量的情况(幻读:避免幻读需要锁表)。但是MVCC就没有幻读问题,postgre不存在幻读问题,pg中这一级别称之为snapshot快照。

可序列化

最高的隔离级别。可序列化意味着数据库中所有的操作都是串行执行的,几乎没有数据库会实现这一级别。

MVCC

大多数事务型数据库引擎实现的都不是简单的行级锁,基于并发性能的考虑因此实现了MVCC(多版本并发控制)

postgre实现了非阻塞的读操作,在MVCC中读不会阻塞写,写也不会阻塞读。MVCC保证了在每个事务内,事务看到的数据是一致的。但是不同事务对同一张表看到的数据可能是不一致的。

MVCC分为乐观并发控制和悲观并发控制。

乐观锁/悲观锁

悲观锁多用于写入频繁的业务,实现较为简单,直接把需要修改的行锁住。其他线程谁也不能进行更改。

乐观锁多用于读取频繁的业务,实现使用了版本号的思想。当版本号一致时,才进行写入。

MVCC解决/没解决哪些问题

因为对行上排他锁后,读写就冲突了。因此MVCC本身解决的一大问题就是使得读写不再冲突。

第一类丢失更新/第二类丢失更新

第一类丢失更新:最弱的事务隔离级别,低于未提交读。事务回滚时覆盖了其他提交事务的数据。

第二类丢失更新:每个事务都对相同的数据进行更改,后面的事务对数据的更改会覆盖之前的事务对数据的更改,最后一个事务所做的修改会覆盖之前所有的修改。试图对资源进行上锁来解决第二类丢失更新可能导致死锁问题。

参考资料

  1. Postgre官方文档
  2. 不可重复读和幻读的区别
  3. MVCC解决了什么问题
  4. 关于pg中事务隔离的总结
  5. 怎么理解数据库的四种隔离级别
  6. 乐观锁/悲观锁
  7. 白话讲解四种隔离级别
  8. 第一类/第二类丢失更新``