Currtid 函数与性能问题
对于Oracle ,一条tuple 的 rowid正常是不会变化的(引发row movement的操作除外,如:跨分区迁移update,表收缩),因此,应用设计上可以方便的使用rowid,加快访问速度。对于KingbaseES,同样也有ctid,格式 “(blockid,slotid)”,通过ctid也能快速的访问数据。但问题在于KingbaseES的MVCC机制,使得ctid会随update操作变化,这种情况下,使用ctid有可能访问不到数据。
一、rowid 与 ctid 的差异
与oracle 不同,KingbaseES ctid 会因为 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
我们知道,PG的update操作实际delete and insert 的结合体。对于update操作完成后,在vacuum 之前,原始tuple是包含指向新tuple的ctid。函数 currtid 可以取得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
三、存在问题
从以上例子可以看到,使用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 ,对于SQL:select * from t1 where ctid=currtid('t1'::regclass,'(0,1)'),如果先计算 currtid('t1'::regclass,'(0,1)') 的结果,传给ctid,再执行SQL。在这期间(从即使currtid,到访问到实际的tuple,时间不确定,可能很长,也可能很短,看执行计划),如果该tuple被修改,则可能返回错误的结果(无记录)。如果采用全表,针对每个tuple,currtid('t1'::regclass,'(0,1)') 都要计算一次(volatile,即使参数值相同,不同时间返回的值是不同的),函数 currtid('t1'::regclass,'(0,1)') 的结果运算推迟到tuple访问的同时进行 ,避免了错误的结果。
四、修改函数属性为immutable
对于 currtid('t1'::regclass,'(0,1)') , 不同时刻执行,返回的结果可能不同,因此,修改函数的属性实际的风险是非常大的。
如果把函数的属性改成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了。如果认为数据不一致的风险可以忽略,可以将currtid 赋值给变量方式,这样就无需每行都调用 currtid 函数。具体如下:
test=# declare
test-# v_ctid tid;
test-# v_cnt integer;
test-# begin
test-# select ctid into v_ctid from t1 where relname='t2';
test-# for i in 1..1000 loop
test-# select currtid('t1'::regclass,v_ctid) into v_ctid;
test-# select count(*) into v_cnt from t1 where ctid=v_ctid;
test-# end loop;
test-# end;
test-# /
ANONYMOUS BLOCK
Time: 16.463 ms test=# declare
test-# v_ctid tid;
test-# v_cnt integer;
test-# begin
test-# select ctid into v_ctid from t1 where relname='t2';
test-# for i in 1..1000 loop
test-# select count(*) into v_cnt from t1 where ctid=currtid('t1'::regclass,v_ctid);
test-# end loop;
test-# end;
test-# /
ANONYMOUS BLOCK
Time: 1007.308 ms (00:01.007)
Currtid 函数与性能问题的更多相关文章
- KTL 一个支持C++14编辑公式的K线技术工具平台 - 第四版,稳定支持Qt5编程,zqt5语法升级,MA函数提升性能1000%,更多公式算法的内置优化实现。
K,K线,Candle蜡烛图. T,技术分析,工具平台 L,公式Language语言使用c++14,Lite小巧简易. 项目仓库:https://github.com/bbqz007/KTL 国内仓库 ...
- 程序中使用now()函数对性能的影响
这两天从某平台的慢查询日志中发现了一些很简单的,原本执行时间在0.01-0.03s的SQL,慢查询日志中记录的执行时间在2s左右. 排查后发现,表设计及索引建设均没有什么问题.但是SQL语句中使用了n ...
- 使用timeit测试Python函数的性能
timeit是Python标准库内置的小工具,可以快速测试小段代码的性能. 认识timeit timeit 函数: timeit.timeit(stmt, setup,timer, number) 参 ...
- Oracle对索引列同时使用多个聚合函数的性能问题
Oracle某一数据表tkk715(数据量在一千万左右),对一个索引字段做获取最大值与最小值的聚合函数操作,响应时间较长(超过3秒): 将SQL改写为分别取最大.最小的聚合值,IO和响应时间显著下降到 ...
- 如何优化JQuery each()函数的性能
如果对jQuery这东西只停留在用的层面,而不知其具体实现的话,真的很容易用出问题来.这也是为什么近期我一直不怎么推崇用jQuery,这框架的API设定就有误导人们走上歧途之嫌. 01 $.fn.be ...
- KingbaseES 函数稳定性与SQL性能
背景:客户现场的一次艰苦的调优过程(https://www.cnblogs.com/kingbase/p/16015834.html),让我觉得非常有必要让数据库用户了解函数的不同稳定性属性,及其对于 ...
- SQL SERVER中用户定义标量函数(scalar user defined function)的性能问题
用户定义函数(UDF)分类 SQL SERVER中的用户定义函数(User Defined Functions 简称UDF)分为标量函数(Scalar-Valued Function)和表值函数(T ...
- PHP函数的实现原理及性能分析
前言 在任何语言中,函数都是最基本的组成单元.对于php的函数,它具有哪些特点?函数调用是怎么实现的?php函数的性能如何,有什么使用建议?本文将从原理出发进行分析结合实际的性能测试尝试对这些问题进行 ...
- sum() 函数性能堪忧,列表降维有何良方?
本文原创并首发于公众号[Python猫],未经授权,请勿转载. 原文地址:https://mp.weixin.qq.com/s/mK1nav2vKykZaKw_TY-rtw Python 的内置函数 ...
随机推荐
- 关于Vue在面试中常常被提到的几点(持续更新……)
1.Vue项目中为什么要在列表组件中写key,作用是什么? 我们在业务组件中,会经常使用循环列表,当时用v-for命令时,会在后面写上:key,那么为什么建议写呢? key的作用是更新组件时判断两个节 ...
- WPF开发随笔收录-心电图曲线绘制
一.前言 项目中之前涉及到胎儿心率图曲线的绘制,最近项目中还需要添加心电曲线和血样曲线的绘制功能.今天就来分享一下心电曲线的绘制方式: 二.正文 1.胎儿心率曲线的绘制是通过DrawingVisual ...
- Windows 启动过程
引言 启动过程是我们了解操作系统的第一个环节.了解 Windows 的启动过程,可以帮助我们解决一些启动的问题,也能帮助我们了解 Windows 的整体结构. 以下内容将分为[加载内核].[内核初始化 ...
- Tomcat深入浅出——Servlet(三)
零.HttpServletRequest 上一篇已经介绍了这个接口,现在补充些内容 首先介绍一下作用域: jakarta.servlet.jsp.PageContext pageContext 页面作 ...
- Spring和SpringMVC整合及关系
SpringMVC扼要的讲,就是控制请求和处理.有必要将Spring和SpringMVC整合,否则仅配置SpringMVC并完成Spring的功能,会造成业务逻辑混乱. 简要总结:①原理:采用监听器, ...
- C语言指针-小结
1) 指针变量可以进行加减运算,但是指针变量的加减运算并不是加上或减去一个数,而是跟指针指向的数据类型有关,数据类型在系统中占了多少个字节,指针+1后就向后移动了多少个字节. 2) int *poin ...
- 外贸ERP系统哪些模块比较重要?得具备什么功能?
我国的外贸企业众多,涉及到多个行业,受疫情的影响,部分企业面临着极大的发展难题.而想要更好的在市场当中生存,除了要有更敏锐的市场嗅觉,也要有更大胆的创新.在外贸ERP系统的发展之下,会得到更多企业的青 ...
- SSH远程登录:两台或多台服务器之间免密登录设置
有两台(或多台)同局域网的服务器A:192.168.2.21,B:192.168.2.25.让A,B这两台服务器之间能两两互相免密登录,并且每台服务器都可以自我免密登录(自我免密登录即:ssh loc ...
- 第六天python3 函数、参数及参数解构(一)
函数 作用:结构话编程对代码的最基本的封装,一般按照功能组织一段代码,封装的目的是为了复用,减少冗余代码,代码更加简洁.美观,可读易懂: 分类: 内建函数,如max() reversed()等 库函数 ...
- 开发中常用的两个JSON方法
参考文章:https://juejin.cn/post/6844903711127404557 在前端开发过程中,有两个非常有用的方法来处理 JSON 格式的内容: JSON.parse(string ...