熟悉oracle的人都知道ROWID可用于快速的数据访问,KingbaseES 由于自身MVCC机制的原因,ctid 作为 oracle rowid 的替代方案不合适,但currtid 还是基本可以满足rowid 的功能的。本文向大家介绍如何通过currtid 实现rowid 的功能。

一、Oracle ROWID

Oracle ROWID 记录了tuple的物理存储位置,通过ROWID可以非常快速地访问tuple。因此,在极致性能的应用设计里,经常会使用到ROWID。典型的使用场景如下:

declare

cursor cur01 as select rowid from tab1;

begin

......

update tab1 where rowid='xxx';

end;

二、KingbaseES CTID

我们知道,对于Oracle ,一条tuple的ROWID正常是不会变化的(引发row movement的操作除外,如:跨分区迁移update,表shrink),因此,应用设计上可以方便的使用rowid,加快访问速度。而对于KingbaseES,也有类似Oracle ROWID的ctid,格式 “(blockid,slotid)”,同样记录了tuple存储的物理位置,通过ctid也能快速的访问数据。但由于KingbaseES的多版本(MVCC)读实现机制的差异,ctid会随update操作变化,这种情况下,使用ctid有可能因为tuple被update,导致访问不到数据。为了让大家对于ctid有直观认识,举例如下:

A用户 B用户
select ctid from t1 where id=1;返回 (0,1)
select ctid from t1 where id=1;返回 (0,1)
update t1 set name='aa' where ctid='(0,1)';
select ctid from t1 where id=1;返回 (0,2)
select * from t1 where ctid='(0,1)'; 无返回

可以看到,在有并发的情况下,用ctid访问是不可靠的。例子中,B用户通过ctid 访问时,就会发现找不到数据。

三、currtid 函数

KingbaseES的update操作实际delete and insert 的结合体。对于update操作完成后,在vacuum 之前,原始tuple是包含指向新tuple的ctid。函数 currtid 可以通过旧ctid 取得updated tuple的最新ctid。具体见以下例子:

test=# insert into t1 values(1,'a');
INSERT 0 1
test=# select ctid from t1 where id=1;ctid
-------(0,1)
(1 row)test=# update t1 set name='aa' where id=1;
UPDATE 1
test=# select ctid from t1 where id=1;ctid
-------(0,2)
(1 row)test=# select * from t1 where ctid='(0,1)';id | name
----+------
(0 rows)test=# select currtid('t1'::regclass,'(0,1)');currtid
---------(0,2)
(1 row)test=# select * from t1 where ctid=currtid('t1'::regclass,'(0,1)');id |   name
----+-----------1 | aa
(1 row)

可以看到,通过将初始的 ctid 传递给 currtid 函数,可以取得最新的 ctid 。

Note:currtid 有效的前提是update 后,多版本信息没有被清理掉,也就是没有进行vacuum操作。

四、性能问题

从以上例子可以看到,使用currtid 可以避免期间数据被修改的问题。但实际上,这里有个性能的问题。请看实际例子:

test=# explain select * from t1 where ctid=currtid('t1'::regclass,'(0,1)');QUERY PLAN
--------------------------------------------------------Seq Scan on t1  (cost=0.00..26.95 rows=1 width=44)Filter: (ctid = currtid('16387'::oid, '(0,1)'::tid))
(2 rows)test=# explain select * from t1 where ctid='(0,2)';QUERY PLAN
---------------------------------------------------Tid Scan on t1  (cost=0.00..4.01 rows=1 width=44)TID Cond: (ctid = '(0,2)'::tid)
(2 rows)

可以看到,对于 ctid=currtid('t1'::regclass,'(0,1)') ,实际上采取的是 seqscan 。问题是currtid('t1'::regclass,'(0,1)') 是在等式右边的,不涉及 ctid 的转换,为什么无法使用 Tid Scan ?

我们来看currtid 函数属性:

test=# select proname,provolatile from pg_proc where proname='currtid';proname | provolatile
---------+-------------currtid | v

函数是 volatile 。volatile 函数导致无法使用TID scan

五、修改函数属性为immutable

把函数的属性改成immutable 情况下的执行计划:

test=# update pg_proc set provolatile='i' where proname='currtid';
UPDATE 1
test=# explain select * from t1 where ctid=currtid('t1'::regclass,'(0,1)');QUERY PLAN
---------------------------------------------------Tid Scan on t1  (cost=0.00..4.01 rows=1 width=44)TID Cond: (ctid = '(0,2)'::tid)
(2 rows)

可以看到,修改函数的属性为 immutable后,可以走 Tid Scan了。

附:volatile 函数与 immutable函数差异

就本例而言,对于SQL:select * from t1 where ctid=currtid('t1'::regclass,'(0,1)', '(0,1)' )。

如果currtid是volatile 类型的函数,优化器采取 Seq Scan,针对每个tuple,都会执行一次函数调用。函数调用是在访问tuple之后,因此,能够保证数据的绝对准确性。

如果currtid是immutable 类型的函数,针对整个查询,只需调用一次函数。执行SQL时,先执行函数,再将结果以参数形式传给SQL。这里的风险点是,如果从函数调用开始到SQL执行完成之前,如果tuple被update,可能导致返回结果的不准确。幸运的是,无论函数调用,还是TID scan,都是非常快的(微秒级别),基本可以避免影响。

当然,如果一定要考虑结果的绝对准确,实际不管使用ROWID,还是 ctid , 都不是绝对安全。因为,即使oracle ,ROWID 也有可能发生变动。

NOTE:以上的例子同时在 PostgreSQL12 和 KingbaseES V8R6进行过验证。

从计算 currtid('t1'::regclass,'(0,1)') 的结果,传给ctid,再执行SQL。在这期间(从即使currtid,到访问到实际的tuple,时间不确定,可能很长,也可能很短,看执行计划),如果该tuple被修改,则可能返回错误的结果(无记录)。如果采用全表,针对每个tuple,currtid('t1'::regclass,'(0,1)') 都要计算一次(volatile,即使参数值相同,不同时间返回的值是不同的),函数 currtid('t1'::regclass,'(0,1)') 的结果运算推迟到tuple访问的同时进行 ,避免了错误的结果。

KingbaseES CTID 与 Oracle ROWID相关推荐

  1. oracle rowid and postgresql ctid

    首先介绍一下oracle rowid,数据库表行中的物理标识 SQL> select rowid from book_info where rownum<=1; ROWID ------- ...

  2. oracle由rowid查找表,Oracle ROWID与RDBA

    Oracle ROWID与RDBA 1.1     Rowid的意义 1.      Rowid就是用来标记表中每一行的行地址的,分为逻辑rowid,物理rowid和外部rowid,一般索引组织表的行 ...

  3. oracle rowID切片,Oracle中ROWID详解

    Oracle的ROWID用来唯一标识表中的一条记录,是这条数据在数据库中存放的物理地址. Oracle的ROWID分为两种:物理ROWID和逻辑ROWID.索引组织表使用逻辑ROWID,其他类型的表使 ...

  4. oracle rowID切片,Oracle中的rowid

    ROWID是ORACLE中的一个重要的概念.用于定位数据库中一条记录的一个相对唯一地址值.通常情况下,该值在该行数据插入到数据库表时即被确定且唯一.ROWID它是一个伪列,它并不实际存在于表中.它是O ...

  5. Oracle rowid和rownum的区别

    rowid和rownum在本质上有区别: rowid rowid是物理结构上的,每插入一行数据,都会生成一条唯一的编号.可以说默认排序是根据rownum升序的,但是本质上还是根据rowid升序排列的. ...

  6. oracle rowid与rownum的使用

    一.rowid的定义:  1.rowid是数据库的一个伪列,建立表的时候数据库会自动为每个表建立ROWID列,是数据库中每一条记录的唯一标识,存储每条记录的实际物理地址,对记录的访问是基于ROWID. ...

  7. oracle的rowid和rownum,【oracle rowid与rownum的使用与区别 】

    一.rowid的定义: 1.rowid是数据库的一个伪列,建立表的时候数据库会自动为每个表建立ROWID列,是数据库中每一条记录的唯一标识,存储每条记录的实际物理地址,对记录的访问是基于ROWID.但 ...

  8. Oracle RowID与RowNum的区别

    Rowid和Rownum对于数据库开发人员来说基本很少用到,因为在企业数据库开发中大多都是进行数据批处理,但是对于其他数据库人员来说还是会用到的. rowid和rownum都是虚列,但含义完全不同.r ...

  9. oracle return rowid,Oracle rowid 详解

    本文讨论的是关于oracle从8i开始引进object的概念后的rowid,即扩展(extended)的rowid: Oracle6,ROWID中仅用6 bit代表文件号Oracle8,ROWID组成 ...

最新文章

  1. Json在线格式化站点
  2. python图片-Python图像处理
  3. hive insert报错return code 1 from org.apache.hadoop.hive.ql.exec.StatsTask (state=08S01,code=1)
  4. 第二十六期:100 个网络基础知识普及,看完成半个网络高手
  5. Java PushbackReader mark()方法与示例
  6. 电商后台原型 rp_电商选型:一站式线上商城的重要性
  7. 初入职场,如何快速脱颖而出?
  8. adb工具的基本使用
  9. VIVO X5M手机ROOT权限获取方法
  10. linux中tftp怎么配置文件,linux的tftp命令参数及用法详解
  11. 微信协议简单调研笔记 (2)
  12. 大数据与数据分析概述
  13. OAuth2四种不同的标准模式
  14. 木讷的程序员需要知道的事情 (四)
  15. 用计算机撩人套路,各种撩人的套路句子40句
  16. 在SOLIDWORKS中如何建立基准面
  17. 解释:commission errors(错分误差) and omission errors(漏分误差)
  18. 浅谈umi router
  19. 测量的基准面和基准线
  20. Web端实现邮件发送

热门文章

  1. 建议大家在入职前背调公司,现在公司坑很多,腾出来的社招岗位多数是前人留下的坑!...
  2. 怀念三国志九:论四大蛮族
  3. 基于Autoware制作高精地图(三)
  4. 智慧安保三维可视化决策系统平台(数字孪生)-解决方案开发案例
  5. 字库表、编码字符集、字符编码
  6. 创建第一个nodejs项目
  7. Mybatis笔记一
  8. v42.05 鸿蒙内核源码分析(中断切换) | 系统因中断活力四射 | 百篇博客分析鸿蒙源码
  9. 小白学CNN以及Keras的速成
  10. 病毒virus(拓扑排序)