KingbaseES CTID 与 Oracle ROWID
熟悉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的更多相关文章
- oracle rowid 详解
oracle rowid详解 今天是2013-09-15,存储在数据库中的每一行数据都有一个地址,oracle使用rowid数据类型在存储地址.rowid有如下类别: 1)physical rowid ...
- ORACLE rowid,file# 和 rfile#
rowid简介 rowid就是唯一标志记录物理位置的一个id,在oracle 8版本以前,rowid由file#+block#+row#组成,占用6个bytes的空间,10 bit 的 file# , ...
- Oracle rowid
本文讨论的是关于oracle从8i开始引进object的概念后的rowid,即扩展(extended)的rowid:1.rowid的介绍先对rowid有个感官认识:SQL> select ROW ...
- oracle rowid 使用
ROWID是数据的详细地址,通过rowid,oracle可以快速的定位某行具体的数据的位置. ROWID可以分为物理rowid和逻辑rowid两种.普通的堆表中的rowid是物理rowid,索引组织表 ...
- Oracle ROWID具体解释
1.ROWID定义 ROWID:数据库中行的全局唯一地址 对于数据中的每一行,rowid伪列返回行的地址.rowid值主要包括下面信息: 对象的数据对象编号 该行所在的数据文件里的数据块 该行中数据块 ...
- oracle rowid 研究
SQL> create table tab01(id integer,val varchar(4)); Table created. SQL> insert into tab01 valu ...
- KingbaseES V8R6兼容Oracle的exp-imp导出导入工具使用
说明: KingbaseES V8R6版本中的兼容Oracle的exp-imp导入导出工具,支持完全模式.用户模式和表模式的导出功能. 本次案例数据库版本: test=# select version ...
- KingbaseES 如何实现Oracle pipelined 功能
管道函数即是可以返回行集合(可以使嵌套表nested table 或数组 varray)的函数,我们可以像查询物理表一样查询它或者将其赋值给集合变量.KingbaseES 数据库可以用 setof 实 ...
- ORACLE rowid切分大表
通过如下sql获取rowid切分范围 ) || dbms_rowid.rowid_create(, DOI, lo_fno, lo_block, ) ) || ) || dbms_rowid.rowi ...
随机推荐
- vmware修改虚拟机网卡mac地址
选中"虚拟机" 右键 "设置",然后选中"网络适配器",然后点击"高级",设置"MAC地址"
- 『现学现忘』Git后悔药 — 29、版本回退git reset --mixed命令说明
git reset --mixed commit-id命令:回退到指定版本.(mixed:混合的,即:中等回退.) 该命令不仅修改了分支中HEAD指针的位置,还将暂存区中数据也回退到了指定版本. 但是 ...
- Collection子接口:Set接口
1.Set 存储的数据特点:无序的.不可重复的元素具体的:以HashSet为例说明: 1. 无序性:不等于随机性.存储的数据在底层数组中并非照数组索引的顺序添加,而是根据数据的哈希值决定的. 2. 不 ...
- java的elasticsearch做高亮显示
import org.apache.commons.lang3.reflect.FieldUtils;import org.elasticsearch.action.search.SearchResp ...
- 可落地的DDD(7)-战术设计上的一些误区
背景 几年前我总结过DDD战术设计的一些落地经验可落地的DDD(5)-战术设计,和一次关于聚合根的激烈讨论最近两年有些新的落地体验,回过头来发现,当初对这些概念的理解还是没有深入,这篇文章重新阐述下. ...
- Apache Hudi数据跳过技术加速查询高达50倍
介绍 在 Hudi 0.10 中,我们引入了对高级数据布局优化技术的支持,例如 Z-order和希尔伯特空间填充曲线(作为新的聚类算法),即使在经常使用过滤器查询大表的复杂场景中,也可以在多个列而非单 ...
- Centos7安装最新docker
Centos7安装最新docker(root身份运行) 环境查看 CentOS 需要7版本以上,内核最好3.10以上 1.查看Linux版本:rpm -q centos-release 2.查看内核版 ...
- ACWing94.递归实现指数型枚举
https://www.acwing.com/problem/content/description/94/ 题面 \92. 递归实现指数型枚举 从 1∼n 这 n 个整数中随机选取任意多个,输出所有 ...
- B.E.M 规范
BEM文档 BEM: A New Front-End Methodology 如何看待 CSS 中 BEM 的命名方式? Battling BEM CSS: 10 Common Problems An ...
- go-zero单体服务使用泛型简化注册Handler路由
一.Golang环境安装及配置Go Module https://go-zero.dev/cn/docs/prepare/golang-install mac OS安装Go# 下载并安装Go for ...