PostgreSQL中如何对应SQL Server中的rowversion

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: SQL Server中有个rowversion,利用它可以实现乐观锁策略的并发更新。那么在PostgreSQL中有没有类似的东西呢? PostgreSQL中,最接近rowversion的就是系统隐藏列xmin。
SQL Server中有个rowversion,利用它可以实现乐观锁策略的并发更新。那么在PostgreSQL中有没有类似的东西呢?

PostgreSQL中,最接近rowversion的就是系统隐藏列xmin。而且在hibernate的PostgreSQL方言中,也是使用xmin作为行版本的标识使用。

xmin是插入该行版本的事务标识(事务ID)。PostgreSQL每次更新行都会创建一个新的行版本,所以如果其他事务修改了之前看到的记录,那么这条记录的xmin必然会变更。

点击(此处)折叠或打开

  1. postgres=# create table tb1(id int,name text);
  2. CREATE TABLE
  3. postgres=# insert into tb1 values(1,'a');
  4. INSERT 0 1
  5. postgres=# update tb1 set name='b' where id=1;
  6. UPDATE 1
  7. postgres=# select xmin,* from tb1;
  8.  xmin | id | name
  9. -------+----+------
  10.  55815 | 1 | b
  11. (1 row)

但是使用xmin作为行版本标识不能区别同一个事务内的两次修改。

点击(此处)折叠或打开

  1. postgres=# begin;
  2. BEGIN
  3. postgres=# update tb1 set name='c' where id=1;
  4. UPDATE 1
  5. postgres=# select xmin,* from tb1;
  6.  xmin | id | name
  7. -------+----+------
  8.  55819 | 1 | c
  9. (1 row)

  10. postgres=# update tb1 set name='c' where id=1;
  11. UPDATE 1
  12. postgres=# select xmin,* from tb1;
  13.  xmin | id | name
  14. -------+----+------
  15.  55819 | 1 | c
  16. (1 row)

  17. postgres=# end;
  18. COMMIT

不过,其实我们不需要为这个小小的瑕疵担心。
因为,事务内的第一次修改对其他事务不可见,唯一能看见它的只有修改这一行的事务自己。如果认为事务自己的第二次修改和自己的第一次修改冲突是不是有点荒谬。所以我们可以认为事务的第二次修改覆盖第一次修改是应用自己愿意。

好了,还真有钻牛角尖的。以上的差异毕竟导致了和SQL Server的rowversion的行为上的微不足道的差异,但为了避免在解释这种差异上多费口舌,我们试图找一种和SQL Server完全一致的方案。

首先我们想到了xmin+cmin。cmin代表了插入事务内部的命令标识。xmin+cmin的组合不就完美了吗。
先不考虑,组合两个字段作为行版本标识在使用上的不便,这个方法还有很大的漏洞。
因为在PostgreSQL内部,cmin和cmax使用的是共用的同一个存储域,就好像C语言中的联合。所以更新和删除操作也会修改cmin。
下面这个例子中一个回滚的更新修改了cmin,如果把cmin作为行版本号一部分使用,就会误判断为发生更新冲突了。

点击(此处)折叠或打开

  1. postgres=# select xmin,cmin,cmax,* from tb1;
  2.  xmin | cmin | cmax | id | name
  3. -------+------+------+----+------
  4.  55822 | 0 | 0 | 1 | c
  5. (1 row)

  6. postgres=# begin;
  7. BEGIN
  8. postgres=# insert into tb1 values(2,'a');
  9. INSERT 0 1
  10. postgres=# update tb1 set name='d' where id=1;
  11. UPDATE 1
  12. postgres=# rollback;
  13. ROLLBACK
  14. postgres=# select xmin,cmin,cmax,* from tb1;
  15.  xmin | cmin | cmax | id | name
  16. -------+------+------+----+------
  17.  55822 | 1 | 1 | 1 | c
  18. (1 row)

除此以外,还有一个候选是ctid。但是每次VACUUM FULL之后, 一个行的ctid都会被更新或者移动。所以如果能够容忍VACUUM FULL带来的更新冲突的误判断,也可以考虑。

转了一圈,最后我还是认为xmin是作为行版本号的最佳方案。


参考:
关于PostgreSQL中的几个系统隐藏列,可参考手册
http://58.58.27.50:8079/doc/html/9.3.1_zh/ddl-system-columns.html
------------------------------------------------------------------------

5.4. 系统字段

每个表都有几个系统字段,这些字段是由系统隐含定义的。因此, 这些名字不能用于用户定义的字段名。请注意这些限制与这个名字是否关键字无关, 把名字用引号括起来并不能让你逃离这些限制。你实际上不需要注意这些字段; 只要知道它们存在就可以了。


oid

行对象标识符(对象ID)。这个字段只有在创建表的时候使用了WITH OIDS 或者是配置参数default_with_oids的值为真时出现。 这个字段的类型是oid(和字段同名);参阅Section 8.18获取有关这种类型的更多信息。

tableoid

包含本行的表的OID。这个字段对那些从继承层次中选取的查询特别有用(参阅Section 5.8), 因为如果没有它的话,我们就很难说明一行来自哪个独立的表。tableoid 可以和pg_class的oid字段连接起来获取表名字。

xmin

插入该行版本的事务标识(事务ID)。注意:在这个环境里,一个行版本是一行的一个状态; 一行的每次更新都为同一个逻辑行创建一个新的行版本。

cmin

在插入事务内部的命令标识(从零开始)。

xmax

删除事务的标识(事务ID),如果不是被删除的行版本,那么是零。在一个可见行版本里, 这个字段有可能是非零。这通常意味着删除事务还没有提交,或者是一个删除的企图被回滚掉了。

cmax

删除事务内部的命令标识符,或者是零。

ctid

一个行版本在它所处的表内的物理位置。请注意,尽管ctid 可以用于非常快速地定位行版本,但每次VACUUM FULL之后, 一个行的ctid都会被更新或者移动。因此ctid 是不能作为长期的行标识符的。应该使用 OID ,或者更好是用户定义的序列号,来标识一个逻辑行。

------------------------------------------------------------------------



相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
4天前
|
SQL 人工智能 算法
【SQL server】玩转SQL server数据库:第二章 关系数据库
【SQL server】玩转SQL server数据库:第二章 关系数据库
40 10
|
1月前
|
SQL 数据库 数据安全/隐私保护
Sql Server数据库Sa密码如何修改
Sql Server数据库Sa密码如何修改
|
2月前
|
SQL 算法 数据库
【数据库SQL server】关系数据库标准语言SQL之数据查询
【数据库SQL server】关系数据库标准语言SQL之数据查询
95 0
|
2月前
|
SQL 算法 数据库
【数据库SQL server】关系数据库标准语言SQL之视图
【数据库SQL server】关系数据库标准语言SQL之视图
72 0
|
14天前
|
SQL
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
15 0
|
4天前
|
SQL 算法 数据库
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
50 6
|
4天前
|
SQL 存储 数据挖掘
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
服务器数据恢复环境: 一台安装windows server操作系统的服务器。一组由8块硬盘组建的RAID5,划分LUN供这台服务器使用。 在windows服务器内装有SqlServer数据库。存储空间LUN划分了两个逻辑分区。 服务器故障&初检: 由于未知原因,Sql Server数据库文件丢失,丢失数据涉及到3个库,表的数量有3000左右。数据库文件丢失原因还没有查清楚,也不能确定数据存储位置。 数据库文件丢失后服务器仍处于开机状态,所幸没有大量数据写入。 将raid5中所有磁盘编号后取出,经过硬件工程师检测,没有发现明显的硬件故障。以只读方式将所有磁盘进行扇区级的全盘镜像,镜像完成后将所
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
|
8天前
|
SQL 安全 Java
SQL server 2017安装教程
SQL server 2017安装教程
13 1
|
21天前
|
SQL 存储 Python
Microsoft SQL Server 编写汉字转拼音函数
Microsoft SQL Server 编写汉字转拼音函数
|
1月前
|
SQL 存储 数据库
数据安全无忧,SQL Server 2014数据库定时备份解密
数据安全无忧,SQL Server 2014数据库定时备份解密