一、了解pg表的构造¶
1.1 表中的系统字段¶
pg中 每个表都有多个系统字段,这些字段是由系统隐式定义的 记住点:因为表中已隐含这些名字的字段,所以用户定义的名称不能与这些字段的名称相同
系统字段
oid:行对象标识符(对象ID)
不一定有:需要创建表时使用了“with oids”或配置参数“default_with_oids”的值为真
tableoid:包含本行的表的oid
xmin:插入该行版本的事务ID。
xmax:删除此行时的事务ID
如果查询出来此字段不为0,则可能是删除这行的事务还未提交,或者是删除此行的事务回滚了
cmin:事务内部的插入类操作的命令ID
cmax:事务内部的删除类操作的命令ID
ctid:一个行版本在它所处的表内的物理位置
1.2 重点讲解oid、xmin、xmax、cmin、cmax、ctid¶
1.2.1 oid¶
PostgreSQL在内部使用对象标识符(OID)作为各种系统表的主键 用户可以在建表时使用“with oids”选项为表增加oid字段
oid的问题:
oid类型用一个4字节的无符号整数实现,不能提供大数据范围内的唯一性保证,甚至在单个的大表中也不行
不同表的oid字段生成的序列值是全局的,就好像所有的oid都使用了一个全局的序列,
create table t3(id int ) with oids;
14以后
create table t3(id int) with(oids=true);
1.2.2 ctid¶
ctid表示数据行在它所处的表内的物理位置,
尽管ctid可以非常快速地定位数据行,但每次VACUUM FULL之后,数据行在块内的物理位置会移动,所以ctid 是不能作为长期的行标识符的
查询
select ctid, id from t limit 10;
查询第10个物理块的第2行内容
select ctid, id from testtab01 where ctid='(10,2)';
利用ctid删除重复数据
create table t (id int);
insert into t values(2),(3),(4),(2),(1),(3);
DELETE FROM t a WHERE a.ctid <> (SELECT min(b.ctid) FROM t b WHERE a.id = b.id);
1.2.3 xmin、xmax、cmin、cmax¶
xmin、xmax、cmin、cmax这4个字段在多版本实现中用于控制数据行是否对用户可见
新插入一行时,将新插入行的xmin填写为当前的事务ID,xmax填“0” insert into t values(8);
elect xmin,xmax,id from t;
修改这一行时,实际上新插入一行,原数据行上的xmin不变,xmax改为当前的事务ID,新数据行上的xmin填为当前的事 务ID,xmax填“0”
update t set id=10 where id=8;
删除一行时,把被删除行上的xmax填写当前的事务ID。
简单理解:
xmin就是标记插入数据行的事务ID,而xmax就是标记删除数据行的事务ID
那“cmin”和“cmax”?
cmin和cmax用于判断同一个事务内的不同命令导致行版本的变化是否可见
二、多版本并发控制(MVCC)¶
2.1 多版本并发控制的原理¶
不一致读问题
用户一:在写数据写到一半 用户二:开始读数据,读到了用户一写到一半的数据, 这时候,用户二读到的数据就是一个不一致的数据 如何解决: 使用读写锁,写的时候不允许读,正在读的时候也不允许写
也带来了问题 无法实现并行 --引入了并行读写的机制-mvcc
2.2 mvcc的实现方式¶
-
写新数据时,把原数据移到一个单独的位置,如回滚段中,其他用户读数据时,从回滚段中把原数据读出 来
-
写新数据时,原数据不删除,而是把新数据插入进来
PostgreSQL数据库使用的是第二种方法 --vacuum机制的诞生
2.3 pg mvcc的实现原理¶
PostgreSQL中的多版本实现是通过把原数据留在数据文件中,新插入一条数据来实现多版本的功能的 每张表中的4个系统字段“xmin”“xmax”“cmin”“cmax”,这4个字段就是为多版本的功能而添加的 当两个事务同时访问记录时,通过参考xmin和xmax的标记判断记录的版本,根据版本号与自己当前的事务标
识进行比较,确定自己的数据权限
当删除数据时,记录并没有从数据块中被删除,空间也没有立即释放 如何释放pg的脏数据空间?
是不是需要想mysql innodb一样需要重建表
答案:肯定是不需要
引入了之前讲的Vaccum进程,默认 PostgreSQL数据库中的AutoVacuum是打开的
当一个表更新量达到一定值时,AutoVacuum自动回收空间
重要点: pg中,若一个事务执行失败,在数据文件中该事务产生的数据并不会在事务回滚时被清理掉
为什么要这样做 直接在事务提交时把这些数据标记成有效,而在事务回滚时把这些数据标记成无效
这里也主要是出于效率的考虑,是pg的优势当然也会有一定劣势-后面会提到 如果不清理的情况下,pg怎么知道这些数据是有效还是无效的?
数据行上记录了xmin和xmax,只需了解xmin和xmax对应的事务是成功提交还是回滚了,就可以知道这些数据 行是否有效
PostgreSQL把事务状态记录在Commit Log中,简称CLOG,CLOG在数据目录的pg_commit_ts子目录下 记录的事务状态有四种:
TRANSACTION_STATUS_IN_PROGRESS=0x00:表示事务正在进行中。 TRANSACTION_STATUS_COMMITTED=0x01:表示事务已提交。 TRANSACTION_STATUS_ABORTED=0x02:表示事务已回滚。 TRANSACTION_STATUS_SUB_COMMITTED=0x03:表示子事务已提交。
2.4 PostgreSQL多版本的优劣¶
优势
-
事务回滚可以立即完成,无论事务进行了多少操作
-
数据可以进行很多更新,不必像Oracle和InnoDB那样需要经常保证回滚段不会被用完
劣势
- 旧版本数据需要清理。PostgreSQL清理旧版本称为VACUUM,并提供了VACUUM命令进行清理
- 旧版本的数据会导致查询更慢一些,因为旧版本的数据存储于数据文件中,查询时需要扫描更多的数据块