暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

从MVCC实现方式看PG应用的问题

白鳝的洞穴 2020-05-13
2206
今天早上老白发现一个WIN10重启提示,系统自动安装了最新的安全补丁,于是重启了电脑,然后就杯具了。幸亏这些年老白的软件正版化做的比较好,除了一些放在D盘的绿色软件外,OFFICE,LIGHTROOM之类的大型软件全部是正版化的,于是安装了一个360,主要目的是为了用360的软件助手,用了一个多小时就把主要的软件全部恢复了。不过等一切恢复,就开始忙了,错过了早上写文章的闲暇时间,只能利用快下班的时间补上。
昨天在一个微信群里在讨论PG的MVCC机制,于是想起了数年前研究过的MVCC的内容,今天拿出来和大家分享一下。
MVCC是Multi-VersionConcurrency Control 的英文缩写,翻译成中文就是多版本并发控制。多版本并发控制的主要目的是为了提高在线事务处理系统(OLTP系统)中的读操作的性能问题。传统的事务理论采用锁机制来实现并发控制,简单的说,写操作使用排它锁,排斥所有的其他操作;读操作使用共享锁,排斥写操作,但是可以支持其他读操作。在MVCC出现之前,如果你的数据有会话在读,那么这个数据是不能修改的,同时某个数据正在被修改,读也会被阻塞。在上世纪80年代前,信息系统的并发并不高,因此这个问题还不是很明显。随着信息化系统的发展,并发访问量越来越大,这种读写互斥锁的机制对并发访问的性能造成了极大的影响。1981年位于马萨诸塞州剑桥的美国计算机公司(Computer Corporationof America)的两位技术人员PHILIP A. BERNSTEIN AND NATHANGOODMAN发表了一篇具有历史意义的论文“Concurrency Control inDistributed Database Systems”,这篇以分布式数据库中并发控制为主要议题的论文中,提出了一种全新的并发控制算法,基于时间戳顺序的多版本并发控制机制(SYNCHRONIZATION TECHNIQUES BASED ON TIMESTAMP ORDERING)。这个全新的并发控制算法为数据库厂商提高并发能力提供了一条新的途径。DEC公司的VAX/RDB 是第一个采用多版本并发控制机制的商用关系型数据库,随后Oracle 4.0也开始支持MVCC,到目前为止,绝大多数商用和开源数据库都已经全面支持多版本并发控制机制,多版本并发控制机制也已经成为交易型关系型数据库的标准配置。

MVCC是通过同一个PAGE或者记录采用具有不同时间戳的副本实现多版本的并发控制。写操作正在写的CURRENT副本并不会被用来做一致性读,未提交的CURRENT副本对于读事务是不可见的。这样就实现了读写之间相互不锁定,可并发执行。

不同数据库实现多版本并发控制的方式是不同的,比如Oracle数据库的使用的是一种类时间戳的机制SCNSystem Change Number),SCN是每个事物提交的序号,是严格按照时间顺序增长的。PostgreSQLMysql则采用了事物IDXID),事物ID也是严格按照时间顺序产生的,因此也满足T/O的基本要求。在MVCC的实现粒度方面,Oracle采用的是页级并发控制(block级别),通过一致读数据页(Consistent ReadBlockCR BLOCK)的机制实现多版本,而PostgreSQLMysql则采用行级多版本控制机制。

 PostgreSQL早期的版本中是不支持多版本并发控制的,因此PostgreSQL的多版本并发控制不是通过类似Oracle的回滚段方式实现的,其实现手段是在数据表中保存某条数据的多个版本。比如说要对某条记录进行修改,并不是直接修改该数据,而是通过插入一条全新的数据,同时对老数据加以标识。而删除数据也不是直接删除该数据,而是在相应的数据行上打上一个删除标识。为了更好的理解这种多版本并发控制机制,我们首先来看一个十分重要的数据结构HeapTupleHeaderDataHeapTupleFieldsHeapTupleHeaderData是每一行数据的头结构,其定义如下:

HeapTupleFields是用于多版本并发控制的核心数据结构,其定义如下:

HeapTupleFields结构是PostgreSQL实现MVCC的核心数据结构,xmin,xmax,cmin,cmaxxvac是其中最为关键的字段。下面我们通过数据结构来看看PostgreSQLMVCC工作机制。由于本文并不是分析PostgreSQLMVCC机制原理的,因此仅仅对PostgreSQLMVCC机制的基本原理进行分析,不会涉及其深次的工作原理。

当一条记录被插入到数据库时,其xmin字段被存储为本事务的XIDxmax0,当事务提交后,所有的事务的XID大于等于xmin中存储的XID的事务,都可以看到这条记录。这完全符合readcommit事务隔离级别的要求。

如果该记录被删除,在PostgreSQL中,暂时不会删除这条记录,而是会在这条记录上做一个标识。PostgreSQL的做法是将该记录的xmax设置为删除这条记录的事务的XID。这样,所有的该记录删除后的事务的XID都大于xmax的值,因此删除后发起的查询都无法读取到这条记录;而对于删除这条记录之前的启动的查询,由于XID小于xmax,因此仍然可以读取到这条记录。这样就解决了MVCC的事务隔离和一致性读的问题。

如果该记录被修改(update)了,那么PostgreSQL不会直接修改原有的记录,而是会生成一条新的记录,新记录的xminupdate操作的XIDxmax0,同时会将老记录的xmax设置为当前操作的XID,也就是说新记录的xmin和老记录的xmax相同。这样在同一张表中,同一条记录就会有存在多个副本。

由于数据多副本的存在,在索引中也会产生类似的多副本情况,因此当DML操作产生时,索引页会做相关的调整。

从PostgreSQL的MVCC机制工作原理来看,INSERT操作并没有太多的问题,PostgreSQL的INSERT操作和其他数据库的工作原理十分类似,只是PostgreSQL的行头大小为20字节,远远大于Oracle的3字节,因此PostgreSQL的存储额外开销要略大于Oracle。从UPDATE操作来看,无论UPDATE多少个字段,PostgreSQL都需要插入一条新的记录,这样会造成SEGMENT高水位的增长,如果某张表的数据插入后,需要多次UPDATE,那么这张表的高水位会出现暴涨。为了解决这个问题,PostgreSQL使用了一个版本回收机制----VACUUM。通过VACUUM,PostgreSQL可以回收旧版本,从而避免多版本带来的性能问题。下面通过Bruce Monjian的一组示意图来回顾一下PostgreSQL的MVCC机制以及VACUUM。
我们先来看一个简单的场景,在一个数据块中有3条记录,如下图:

当Tuple1和Tuple2被删除后,数据块中的数据如下图:

这时候索引指向的块头中的Tuple Item还没被清理,因此记录索引表的内容还不能复用。必须经过VACUUM才能完成这个工作,VACUUM后,块内的的情况如下图

从上图,我们可以看出经过VACUUM,PostgreSQL可以有效的进行空间的回收,不过普通的VACUUM仅仅能够回收数据块内的空间,无法降低高水位。要降低高水位,必须进行一种称为VACUUM FULL的操作,这个操作类似于Oracle的SHRINK后回收高水位的操作,可以对PostgreSQL的表进行空间回收和降低高水位的操作。不过VACUUM FULL操作和Oracle的Shrink Table操作不完全类似,VACUUM FULL操作在较长的一段时间内采用排他方式锁定表,直到操作完成。VACUUM FULL操作如此高的成本,使7*24系统中经常性进行VACUUM FULL操作的可能极低,那么不经常性进行VACUUM FULL操作,会对系统带来什么影响呢?
从PostgreSQL的MVCC机制上来看,对于经常有DML操作的表来说,其高水位会有较大的变化。甚至可能达到正常水平的数倍、数十倍。这样会影响全表扫描的性能。由于表本身的碎片,也会引起表上索引出现不正常增长。这种增长也会影响范围扫描和索引唯一扫描的性能。在实际应用环境中,这种碎片对PostgreSQL的操作产生较大的影响。
PostgreSQL数据库在大量DELETE、UPDATE操作后,表和索引都会出现高水位大幅提升的问题。从而导致索引唯一性访问、索引范围扫描和全表扫描性能的下降,而且下降的幅度还是比较大的。经过VACUUM后,高水位无法恢复,不过索引访问和全表扫描的性能都可以大幅度恢复。
PostgreSQL使用了一种十分简单的方法实现了多副本控制机制,并没有像Oracle、MYSQL一样引入专门的回滚段机制。这种MVCC机制实现十分简单,但是我们可以很快发现这种机制存在的问题,因为update操作会在表中产生大量的MVCC版本,从而导致表和索引碎片的大量产生,从而影响该表访问的性能。不过这种MVCC机制也不都是副作用,对于删除操作,由于PostgreSQL只需要简单的标识,因此大批量删除的场景,PostgreSQL的性能优于Mysql。由于MVCC机制的缺陷,PostgreSQL数据库的应用场景也受到了一定的限制,像我们在应用系统中经常使用的经常需要更新的余额表,运维自动化系统中的某个指标的当前状态数据,如果更新的频率十分高,那么使用PG数据库的时候,要考虑到其可能产生的性能问题。尽可能把这些数据放到内存缓冲中,定期进行批量更新。并且要定时对这些表做VACUUM操作,甚至预留一些时间窗口,做VACUUM FULL的整理工作。

最后修改时间:2020-05-14 08:14:42
文章转载自白鳝的洞穴,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论