导语
成都创新互联专注为客户提供全方位的互联网综合服务,包含不限于网站建设、网站制作、城口网络推广、微信小程序开发、城口网络营销、城口企业策划、城口品牌公关、搜索引擎seo、人物专访、企业宣传片、企业代运营等,从售前售中售后,我们都将竭诚为您服务,您的肯定,是我们最大的嘉奖;成都创新互联为所有大学生创业者提供城口建站搭建服务,24小时服务热线:13518219792,官方网址:www.cdcxhl.com
PostgreSQL是通过MVCC(Multi-Version Concurrency Control)来保证事务的原子性和隔离性,具体MVCC机制是怎样实现的,下面举些示例来做个简单解析以加深理解。
前提
表中隐藏的系统字段
PostgreSQL的每个表中都有些系统隐藏字段,包括:
MVCC机制
MVCC机制通过这些隐藏的标记字段来协同实现,下面举几个示例来解释MVCC是如何实现的
- //seesion1:
- 创建表,显示指定oid字段:
- testdb=# create table t1(id int) with oids;
- CREATE TABLE
- 插入几条记录
- testdb=# insert into t1 values(1);
- INSERT 17569 1
- testdb=# insert into t1 values(2);
- INSERT 17570 1
- testdb=# insert into t1 values(3);
- INSERT 17571 1
查询当前表中的tuple信息,xmin为创建tuple时的事务ID,xmax默认为0
- testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1;
- ctid | xmin | xmax | cmin | cmax | oid | id
- -------+----------+------+------+------+-------+----
- (0,1) | 80853357 | 0 | 0 | 0 | 17569 | 1
- (0,2) | 80853358 | 0 | 0 | 0 | 17570 | 2
- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3
- (3 rows)
接下来,我们更新某个tuple的字段,将tuple中id值为1更新为4,看看会发生什么
- testdb=# begin;
- BEGIN
- testdb=# select txid_current();
- txid_current
- --------------
- 80853360
- (1 row)
- testdb=# update t1 set id = 4 where id = 1;
- UPDATE 1
查看tuple详细信息
- testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1;
- ctid | xmin | xmax | cmin | cmax | oid | id
- -------+----------+------+------+------+-------+----
- (0,2) | 80853358 | 0 | 0 | 0 | 17570 | 2
- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3
- (0,4) | 80853360 | 0 | 0 | 0 | 17569 | 4
- (3 rows)
可以看到id为1的tuple(oid=17569)已经被修改了,id值被更新为4,另外ctid、xmin字段也被更新了,ctid值代表了该tuple的物理位置,xmin值是创建tuple时都已经写入,这两个字段都不应该被更改才对,另起一个seesion来看下(当前事务还未提交)
- //seesion2:
- testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1;
- ctid | xmin | xmax | cmin | cmax | oid | id
- -------+----------+----------+------+------+-------+----
- (0,1) | 80853357 | 80853360 | 0 | 0 | 17569 | 1
- (0,2) | 80853358 | 0 | 0 | 0 | 17570 | 2
- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3
- (3 rows)
可以看到id为1的tuple(oid=17569)还存在,只是xmax值被标记为当前事务Id。 原来更新某个tuple时,会新增一个tuple,填入更新后的字段值,将原来的tuple标记为删除(设置xmax为当前事务Id)。同理,可以看下删除一个tuple的结果
- //seesion1:
- testdb=# delete from t1 where id = 2;
- DELETE 1
- //seesion2:
- testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1;
- ctid | xmin | xmax | cmin | cmax | oid | id
- -------+----------+----------+------+------+-------+----
- (0,1) | 80853357 | 80853360 | 0 | 0 | 17569 | 1
- (0,2) | 80853358 | 80853360 | 1 | 1 | 17570 | 2
- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3
- (3 rows)
删除某个tuple时也是将xmax标记为当前事务Id,并不做实际的物理记录清除操作。另外cmin和cmax值递增为1,表明了同一事务中操作的顺序性。在该事务(seesion1)未提交前,其他事务(seesion2)可以看到之前的版本信息,不同的事务拥有各自的数据空间,其操作不会对对方产生干扰,保证了事务的隔离性。
提交事务,查看最终结果如下:
- //seesion1:
- testdb=# commit;
- COMMIT
- testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1;
- ctid | xmin | xmax | cmin | cmax | oid | id
- -------+----------+------+------+------+-------+----
- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3
- (0,4) | 80853360 | 0 | 0 | 0 | 17569 | 4
- (2 rows)
但是,如果我们不提交事务而是回滚,结果又是如何?
- testdb=# begin ;
- BEGIN
- testdb=# update t1 set id = 5 where id = 4;
- UPDATE 1
- testdb=# rollback;
- ROLLBACK
- testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1;
- ctid | xmin | xmax | cmin | cmax | oid | id
- -------+----------+----------+------+------+-------+----
- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3
- (0,4) | 80853360 | 80853361 | 0 | 0 | 17569 | 4
- (2 rows)
- xmax标记并未清除,继续新增一条记录:
- testdb=# insert into t1 values(5);
- INSERT 17572 1
- testdb=# select ctid, xmin, xmax, cmin, cmax, oid, id from t1;
- ctid | xmin | xmax | cmin | cmax | oid | id
- -------+----------+----------+------+------+-------+----
- (0,3) | 80853359 | 0 | 0 | 0 | 17571 | 3
- (0,4) | 80853360 | 80853361 | 0 | 0 | 17569 | 4
- (0,6) | 80853362 | 0 | 0 | 0 | 17572 | 5
- (3 rows)
发现没有清理掉新增的tuple,消除原有tuple上的xmax标记,这是为何?处于效率的原因,如果事务回滚时也进行清除标记,可能会导致磁盘IO,降低性能。那如何判断该tuple的是否有效呢?答案是PostgreSQL会把事务状态记录到clog(commit log)位图文件中,每读到一行时,会到该文件中查询事务状态,事务的状态通过以下四种来表示:
MVCC保证原子性和隔离性
原子性
事务的原子性(Atomicity)要求在同一事务中的所有操作要么都做,要么都不做。根据PostgreSQL的MVCC规则,插入数据时,会将当前事务ID写入到xmin中,删除数据时,会将事务ID写入xmax中,更新数据相当于先删除原来的tuple再新增一个tuple,增删改操作都保留了事务ID,根据事务ID提交或撤销该事务中的所有操作,从而保证了事务的原子性。
隔离性
事务的隔离性(Isolation)要求各个并行事务之间不能相互干扰,事务之间是隔离的。PostgreSQL可读取的数据是xmin小于当前的事务ID且已经提交。对某个tuple进行更新或删除时,其他事务读取的就是这个tuple之前的版本。
MVCC的优势
读写不会相互阻塞,写操作并没有堵塞其他事务的读,在写事务未提交前,读取的都是之前的版本,提高了并发的访问效率。
事务可以快速回滚,操作后的tuple都带有当前事务ID,直接标记clog文件中对应事务的状态就可达到回滚的目的。
MVCC带来的问题
事务ID回卷问题
PostgreSQL也需要事务ID来确定事务的先后顺序,PostgreSQL中,事务被称为XID,获取当前XID:
- testdb=# select txid_current();
- txid_current
- --------------
- 80853335
- (1 row)
事务ID由32bit数字表示,当事务ID用完时,就会出现新的事务ID会比老ID小,导致事务ID回卷问题(Transaction
ID Wraparound)。 PostgreSQL的事务ID规则:
– 大于2的事务ID都是普通的事务ID。
当***和最旧事务之差达到2^31时,就把旧事务换成FrozenXID,然后通过公式((int32)(id1 - id2)) < 0比较大小即可
垃圾数据问题
根据MVCC机制,更新和删除的记录都不会被实际删除,操作频繁的表会积累大量的过期数据,占用磁盘空间,当扫描查询数据时,需要更多的IO,降低查询效率。PostgreSQL的解决方法是提供vacuum命令操作来清理过期的数据。
文章标题:PostgreSQL的MVCC机制解析
文章转载:http://www.shufengxianlan.com/qtweb/news24/279374.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联