PostgreSQL、Oracle、Mysql中MVCC机制详解

1、什么是MVCC?

MVCC,也就是多版本并发控制,是为了在读取数据时不加锁来提高读取效率和并发性的一种手段。

数据库并发有以下几种场景:

  • 读-读:不存在任何问题。
  • 读-写:有线程安全问题,可能出现脏读、幻读、不可重复读。
  • 写-写:有线程安全问题,可能存在更新丢失等。

MVCC解决的就是读写时的线程安全问题,线程不用去争抢读写锁。

1.1、事务隔离级别

  • read uncommitted:脏读,一个事务可以读到另外一个事务未提交的数据,大多数关系型数据库不支持。
  • read committed:提交读,一个事务可以读到其他事务已经提交的数据,大多数数据库的缺省模式。
  • repeatable read:一个事务执行过程中访问的数据是一致的,也就是一个事务中多次读到的数据不会变化。
  • serializable:序列化,事务串行化执行,避免不一致。代价很大,OLTP系统中很少使用。

在大多数关系型数据库中默认的事务隔离级别都是read committed(mysql中默认是repeatable read)。我们也可以根据业务场景的需要去选择合适的隔离级别,例如在一个事务中,使用select for update来避免读到不一致的数据,如果不是为了排他性的锁定,用RR事务隔离级别来代替可以获得更好的性能。

1.2、并发控制介绍

常见的并发控制有:

  1. MVCC:Multi-version Concurrency Control (多版本并发控制)。
  2. S2PL: Strict Two-Phase Locking(严格二阶段锁):读写互斥,保证串行。
  3. OCC:Optimistic Concurrency
    Control(乐观并发控制):和悲观锁不同,乐观锁在提交前不加锁,提交时如果读取时的数据被其他事务修改并提交,则回退事务。

简单来说,在MVCC机制还没有出现之前,数据库中读写是互斥的,而通过MVCC机制使得读写不互斥。因此MVCC使关系型数据库并发读写能力得到很大的提高,而大多数OLTP系统中的85%以上是读操作。

2、Oracle的MVCC实现方式

Oracle的多版本并发控制是基于块级的,利用Oracle UNDO/回滚段机制。在回滚段中保存了某个数据被修改之前的前映像的数据。
当我们在数据中查询之前版本的数据时,oracle是这样做的:首先查询的过程会在undo段中查找该数据块的前映像后,然后把前映像和current块合并形成了一个CR block,通过查询cr block就可以满足数据的一致性了。

正因为数据的前映像通过在DB BUFFER中的CR BLOCK来实现,所以数据无论修改多少次,都不会对存储数据的数据段产生负面的影响。而且一个CR BLOCK生成后,可以在缓冲区中较长时间内存在,供相关的事务使用。这个功能对于大并发的读操作来说,是十分有用的,可以大大提高相关操作的性能。
 

3、Mysql的MVCC实现方式

mysql innodb引擎的MVCC也是通过undo段来实现的,但是和oracle不同的是:mysql的多版本并发控制是基于记录级的,mysql中通过undo来形成行的版本链。

上图中的回滚指针(DB_ROLL_PTR)字段用来指写入回滚段(rollback segment)的 undo log record (撤销日志记录记录)。
而其具体做法就是通过两个隐含列来实现的:一个是db_trx_id,指出该行的事务ID,一个是db_roll_ptr,指出这条记录的pre-image数据在UNDO中的地址。
 

4、PostgreSQL的MVCC实现方式

最后我们再来看看PostgreSQL的MVCC机制,和oracle还有mysql中不同的是,pg中没有undo这一概念,pg中的多版本并发是通过在表中数据行的多个版本来实现的,例如在一张表中我们要更新一条记录,pg并不是直接修改该数据,而是通过插入一条全新的数据,同时对老数据加以标识。
在pg中一个page页结构大致如下图所示:

对应的数据结构为PageHeaderData:

而实现多版本是通过HeapTupleFields:

要想理解pg的多版本机制,首先要弄清楚几个关键的字段:

  • t_xmin:插入该元组的事务的txid;
  • t_xmax:删除或更新该元组的事务的txid,如果尚未删除或更新该元组,则t_xmax设置为0;
  • t_cid:命令ID(cid),这表示从0开始在当前事务中执行此命令之前已执行了多少个SQL命令;
  • t_ctid:指向自身或新元组的元组标识符(tid),当该元组被更新时,该元组的t_ctid指向新的元组,否则,t_ctid指向自身。

关于这些字段的详细介绍见:PostgreSQL表的系统字段

我们可以通过一个简单的例子来看看:

bill=# select xmin,xmax,ctid,* from t;    
 xmin | xmax | ctid  | id | info 
------+------+-------+----+------
  824 |    0 | (0,1) |  1 | a
(1 row)

更新该表中的数据:
可以看到新记录的xmin被置为825,ctid指向新的记录。

bill=# update t set info = 'b' where id=1;
UPDATE 1
bill=# select xmin,xmax,ctid,* from t;    
 xmin | xmax | ctid  | id | info 
------+------+-------+----+------
  825 |    0 | (0,2) |  1 | b
(1 row)

缺点:事务id是2的32次方,数量有限制,用完时会出现wraparound问题,pg通过VACUUM机制解决该问题。

 5、总结

oracle和mysql都是通过undo来实现多版本并发控制,而oracle和mysql还有pg不同的是它的多版本并发控制是基于块级的,而mysql和pg是记录级别的,个人认为这也是为什么mysql中和pg中很难实现oracle中rac的原因——页级别的多版本记录在不同节点之间同步的效率必然比块级别差很多。

同时pg的多版本机制导致pg并不适合频繁update的场景,可能会带来表和索引的高水位大幅提升的问题(PostgreSQL中的“高水位”)。不过定期进行vacuum full等操作还是能够避免大部分问题的。
 

相关文章:

PostgreSQL和Mysql的MVCC实现机制的差异对比

PostgreSQL教程:MVCC

【干货分享】浅析VACUUM清理 

————————————————
版权声明:本文为CSDN博主「foucus、」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_39540651/article/details/106690158

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MVCC(多版本并发控制)是一种常见的数据库并发控制技术,它允许多个事务同时访问数据库,而不会互相干扰。下面是MySQLPostgreSQLOracleMVCC方式的对比: 1. MySQL MVCC方式: MySQL使用基于时间戳的MVCC方式。每个事务在开始时会分配一个唯一的时间戳,然后每个数据行都会存储一个创建时间和一个过期时间。当一个事务要读取一行数据时,MySQL会比较事务的时间戳和该数据行的创建和过期时间,如果该数据行的创建时间早于事务的时间戳,并且过期时间晚于事务的时间戳,则该数据行可见。 2. PostgreSQL MVCC方式: PostgreSQL使用基于快照的MVCC方式。每个事务在开始时会创建一个快照,这个快照包含了数据库所有数据的当前状态。每个数据行都会存储一个版本号和一个指向该版本的指针。当一个事务要读取一行数据时,PostgreSQL会比较事务的快照和该数据行的版本号,如果该数据行的版本号早于事务的快照,则该数据行不可见。 3. Oracle MVCC方式: Oracle使用基于undo的MVCC方式。每个事务在开始时会创建一个undo段,这个undo段用于存储所有被修改的数据的历史版本。每个数据行都会存储一个SCN(系统变更号),这个SCN表示该数据行的版本号。当一个事务要读取一行数据时,Oracle会比较事务的SCN和该数据行的SCN,如果该数据行的SCN早于事务的SCN,则该数据行可见。 总的来说,MySQLMVCC方式相对简单,但是在高并发情况下可能会出现性能问题。PostgreSQLMVCC方式比较复杂,但是可以提供更好的并发性能。OracleMVCC方式则是一种比较成熟的实现,但是需要额外的存储空间来存储undo段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值