你是否留意过数据库中有一个奇怪的函数 TBL$OR$IDX$PART$NUM ,你是否留意过很多场景下都出现过它的身影?

让我们通过几个案例、几个特性,了解一下 Oracle 的隐藏世界。

故事一:ORA-1008 错误诊断

某日,在客户的运行场景下,特定SQL遭遇 ORA-00604 / ORA-01008, 导致某些报表无法正常运行。

数据库版本为 11.2.0.4.4,操作系统 Linux,执行的错误信息如下:

ORA-604: error occurred at recursive SQL level 1

ORA-1008: not all variables bound

用户仔细检查了 n 次 SQL,并未发现绑定变量赋值问题,但是SQL操作的主要表是复合分区表,启用 10046 事件跟踪 或者 Errorstack,可以通过后台日志找到了可疑的问题SQL和详细信息。

原来出现问题的并不是用户SQL,而是在后台执行的一条递归SQL语句,这个数据库自身的SQL语句调用,因为缺少变量输入,而出现了 ORA-01008 错误:

----- Error Stack Dump -----

ORA-01008: not all variables bound

----- Current SQL Statement for this session (sql_id=7ughmqbx14mfz) -----

SELECT distinct TBL$OR$IDX$PART$NUM("TERRY", 0, 2, 0, "ID") FROM (SELECT "B"."ID" "ID" FROM "G_DAW" "B" WHERE "B"."SALE_END_TIME">=TO_DATE(:B1,'yyyy-mm-dd hh24:mi:ss') AND "B"."RDC_ID"=TO_NUMBER(:B2) AND "B"."RDC_ID"=TO_NUMBER(:B3) AND "B"."SALE_BEGIN_TIME"<=TO_DATE(:B4,'yyyy-mm-dd hh24:mi:ss')) ORDER BY 1

注意:在很多数据库问题的诊断过程中,我们都能发现递归SQL的身影,当递归 SQL 出现在 AWR 报告的 TOP SQL中时,就需要注意,是否触发了BUG。

通过 TBL$OR$IDX$PART$NUM 关键字和 ORA-1008 错误号,在 MOS 上很容易找到这个问题的相关BUG:

Bug 17258090  Recursive SQL fails with ORA-1008

这个BUG的描述大致是:

对于复合分区表的某些分区剪裁,可能会发生这种情况。如果子分区级别的子查询剪裁在执行时出现 ORA-604 / ORA-1008,那就可能和这个BUG相关。

其典型特征就是,出错时指向如下SQL:

SELECT distinct TBL$OR$IDX$PART$NUM ....

简单来说,就是Oracle在子分区级别执行子查询分区剪裁时,调用的内部递归SQL,因为缺少变量传入而出错,其解决方案是,禁用子查询谓词分区剪裁。

这个特性是由参数控制的,在会话级别禁用:

alter session

set "_subquery_pruning_enabled"=false;

或者修改初始化参数,在全局禁用之:

alter system

set “_subquery_pruning_enabled”=false;

这个特性是在 Oracle 10g 引入的,在执行计划中,当出现 PARTITION RANGE SUBQUERY ,我们可以看到在 Pstart 和 Pstop 执行了 分区剪裁,就是使用到了这个特性。当禁用这个参数,将会影响这一部分执行计划:

通过 x$ksppi 表,可以找到所有隐含参数,及了解其含义:

SQL> select ksppinm,ksppdesc from x$ksppi where ksppinm='_subquery_pruning_enabled';

KSPPINM

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

KSPPDESC

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

_subquery_pruning_enabled

enable the use of subquery predicates to perform pruning

该参数的作用就是:允许使用子查询谓词来执行分区剪裁。和这个参数相关的还有几个参数,通过参数描述就可以知道其作用:

SQL> select ksppinm,ksppdesc from x$ksppi where ksppinm like '_subquery_pruning%';

KSPPINM

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

KSPPDESC

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

_subquery_pruning_cost_factor

subquery pruning cost factor

_subquery_pruning_reduction

subquery pruning reduction factor

_subquery_pruning_enabled

enable the use of subquery predicates to perform pruning

_subquery_pruning_mv_enabled

enable the use of subquery predicates with MVs to perform pruning

这个BUG的主要影响范围是 12.1.0.1 (Base Release) 和 11.2.0.4 ,大家可以酌情参考。

故事二:MERGE 的缓慢之夜

看完了这个小案例之后,我们想起了近期遇到的另外一个问题,在一个客户的分析系统中,遭遇到性能问题,从 32K 的块大小可以看出,这是一个仓库型应用:

在AWR报告中的 TOP SQL 中,我们注意到前两个SQL语句 MERGE 操作,执行时间超长。第一条 SQL 执行了 167,844 秒,对照AWR的采样时间 780 分钟,接近是 4 倍 的 Elappsed Time:

为什么TOP SQL执行如此之久,并且执行时间( 167844/780/60 = 3.59 )远超单CPU周期 ,我们看一下这条SQL就一目了然了,因为其使用了 4 个并行度,并行执行时间被累计:

MERGE /*+ APPEND NOLOGGING PARALLEL ("TRANS_DEPOSITS", 4) */

INTO "TRANS_DEPOSITS" USING (SELECT ....

这个问题,进一步怎么分析呢?虽然我们可以分析执行计划、阻塞、锁定等相关因素,但是第三个SQL引起了我的关注。通过上一个案例的分析,我们已经不陌生了,这是一条递归SQL,执行时间同样超长,存在问题。

展开这条 SQL ,仔细看一下,显然同样是对于分区表的内部操作:

SELECT distinct TBL$OR$IDX$PART$NUM("TRANS_DEPOSITS", 0, 1, 0, "TRAN_DATE")

FROM

(SELECT "BANS_INCT01"."D2_TRN_DATE" "TRAN_DATE"

FROM "ODS"."BANS_INCT01" "BANS_INCT01") ORDER BY 1

这条SQL有什么特殊之处呢?多看一秒钟

如果注意看,你会发现这个 SQL 的子查询 FROM 子句中没有 WHERE 条件。

有了这些基本分析之后,我们判断这可能是另外一个 BUG 了,结合 TBL$OR$IDX$PART$NUM 关键字 和 MERGE 操作,在 MOS 上很快能够匹配到以下 BUG:

Bug 18794814  MERGE statement is slow 

due to recursive query on TBL$OR$IDX$PART$NUM 

这个 BUG 离我们更近了一步,影响范围是 12.1.0.2 (Server Patch Set) 和 11.2.0.4 ,修正版本是:12.2.0.1 或者 12.1.0.2.170718 。

这个 BUG 的触发条件:

如果发现 MERGE SQL 执行缓慢并跟踪/堆栈,发现如下递归SQL:

SELECT distinct TBL$OR$IDX$PART$NUM(....

并且这个SQL没有连接条件,则可能遇到此BUG。

这个问题的临时解决方案和上一个案例相同:

设置  _subquery_pruning_enabled = false ,或者找到适合的补丁应用。

注意:由于在很多场景下遇到问题,初始化配置数据库时,可以考虑将参数 _subquery_pruning_enabled 设置为 False,以规避可能遇到的种种问题。

故事三:TBL$OR$IDX$PART$NUM 的身世之谜

我们已经反复看到了 TBL$OR$IDX$PART$NUM 的身影,那么现在是时候研究一下这是一个什么函数了。

TBL$OR$IDX$PART$NUM 函数是一个内部函数,用于确定特定记录所属的分区。这个函数经常用于各种内部操作,前面我们已经见到了几个案例。

函数调用的基本格式如下:

TBL$OR$IDX$PART$NUM(PARTITIONED_TABLE_NAME,0,d#,p#,COLUMN_NAME) .

这个函数没有文档说明,需要通过测试来进行一些验证。如下创建测试分区表:

create table enmotech

( depid     number,

kpiid     varchar2(1),

ename     varchar2(10),

npscr     number)

partition by range (depid,kpiid)

subpartition by range (npscr)

subpartition template

( subpartition G1 values less than(60),

subpartition G2 values less than(90),

subpartition G3 values less than(100))

( partition "P1"  values less than (10,'A'),

partition "P2"  values less than (10,'B'),

partition "P3"  values less than (10,'C'),

partition "P4"  values less than (20,'A'),

partition "P5"  values less than (20,'B'),

partition "P6"  values less than (20,'C'));

insert into enmotech values(10, 'A','ANG', 95);

insert into enmotech values(10, 'B','ING', 85);

insert into enmotech values(10, 'C','ION', 75);

insert into enmotech values(20, 'A','HUA', 95);

insert into enmotech values(20, 'B','JUU', 85);

commit;

如果我们想找出 (depid,kpiid)为 (20,'B')的记录所属分区,以下查询即可支持:

select TBL$OR$IDX$PART$NUM("C##EYGLE"."ENMOTECH", 0, 1, 0, 20, 'B') pt

from dual;

PT

----------

6

查询一下表中现有记录及分区情况:

SQL> SELECT en.*,

TBL$OR$IDX$PART$NUM("C##EYGLE"."ENMOTECH", 0, 1, 0,

en."DEPID", en."KPIID") pt

FROM ENMOTECH en;

DEPID K ENAME     NPSCR   PT

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

10 A ANG 95     2

10 B ING 85     3

10 C ION 75     4

20 A HUA 95     5

20 B JUU 85     6

根据ROWID转换对象号出来:

SQL> select

tbl$or$idx$part$num("C##EYGLE"."ENMOTECH",

0,

4,

0,

"ROWID") objn from enmotech  ;

OBJN

----------

96047

96048

96049

96050

96051

有了这个对象号,关联 DBA_OBJECTS 或者 USER_OBJECTS ,可以找到对象名称等信息,整个转换链路就可以非常灵活.

注意,以下查询是在 11.2.0.4 版本上的查询输出,我们可以清晰的看到每一条记录所属的对象和分区:

故事四:来自 12.2 的未知 BUG

再请注意,当我们在 12.2 版本上执行这个SQL时,确触发了内部错误,这是一个未知的BUG,使用的是 12.2 多租户版本,使用 common 用户执行测试:

在告警日志中记录的错误如下,在 MOS 上没有匹配的已知BUG,也请谨慎测试:

Exception [type: SIGSEGV, SI_KERNEL(general_protection)] [ADDR:0x0] [PC:0x10EA885B, lnxsni()+2059] [flags: 0x0, count: 1]

Errors in file /u01/trace/enmo12c_ora_3508.trc  (incident=256928) (PDBNAME=CDB$ROOT):

ORA-07445: exception encountered: core dump [lnxsni()+2059] [SIGSEGV] [ADDR:0x0] [PC:0x10EA885B] [SI_KERNEL(general_protection)] []

检查一下跟踪文件,注意标红部分,最后执行这个递归查询之后,进入SQL执行阶段,出现错误,这个基于view$的查询,传入一个绑定变量 ROWID:

我们来检查一下,这个递归SQL的执行作用。

首先这个ROWID 00002F93.0006.0001 是一个历史版本的限制 ROWID格式,需要转换成扩展 ROWID:

SQL> select DBMS_ROWID.ROWID_TO_EXTENDED

('00002F93.0006.0001','SYS','VIEW$',0)

from dual;

DBMS_ROWID.ROWID_T

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

AAAABfAABAAAC+TAAG

这个SQL返回的对象是什么呢?确认一下:

SQL> select obj# from view$

where rowid=DBMS_ROWID.ROWID_TO_EXTENDED

('00002F93.0006.0001','SYS','VIEW$',0);

OBJ#

----------

4440

SQL> select object_name

from dba_objects where object_id=4440;

OBJECT_NAME

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

_CURRENT_EDITION_OBJ

竟然是 版本对象 惹的祸。

再来检查一下这个SQL查询,其中 enmotech 是一个记录表,dba_objects 是一个复杂视图,显然是在这个视图关联和展开时遇到了和版本对象相关的 BUG,将视图查询结果实体化,可以绕过这个问题:

在这个查询中,类似的结果集可以通过 dbms_rowid.rowid_object 方式获取:

那么除此之外,在哪些场景下还可能用到 TBL$OR$IDX$PART$NUM 这个函数呢?

故事五:12c 的延迟索引维护特性

在 Oracle 12c 中,有一个新特性被称为:延迟全局索引维护 - Delayed Global Index Maintenance 。

大家知道,在分区表中,如果创建全局索引,当我们对分区进行删除、截断等操作时,全局索引会失效,除非增加关键字 UPDATE GLOBAL INDEXES 同时维护索引,但是增加这个关键字又可能导致分区维护过慢。

在 12c 中,增加了一个特性,延迟全局索引维护,可以将索引维护工作异步延迟进行,在数据库中是通过 SYS.PMO_DEFERRED_GIDX_MAINT_JOB 任务,在每天02:00运行。

那么问题来了,延迟维护的全局索引,在查询中还会生效吗?

基于以上测试表创建一个全局索引:

SQL> create index enmotech_idx1 on enmotech (depid) global ;

Index created.

索引在查询中生效:

删除一个分区,再来检查索引状态,在 orphaned_entries 字段已经记录了索引出现孤儿条目:

SQL> alter table enmotech drop partition p2 update indexes;

Table altered.

SQL> COL INDEX_NAME FOR A20

SQL> select index_name, status,orphaned_entries

from dba_indexes

where index_name='ENMOTECH_IDX1';

INDEX_NAME     STATUS   ORP

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

ENMOTECH_IDX1     VALID    YES

注意此时的执行计划,仍然使用了索引,但是多了一个谓词过滤条件,是通过 TBL$OR$IDX$PART$NUM 函数对 ROWID 进行判断过滤,以保证索引中的孤儿条目不会被执行,当然有了这个步骤,我们看到递归调用的数量大大增加了:

我们可以检查一下 PMO_DEFERRED_GIDX_MAINT_JOB 的工作情况,其执行是调用了dbms_part的索引维护包:

SQL>  exec print_table('select JOB_NAME,LAST_START_DATE,RUN_COUNT from dba_scheduler_jobs where job_name=''PMO_DEFERRED_GIDX_MAINT_JOB''');

JOB_NAME       : PMO_DEFERRED_GIDX_MAINT_JOB

LAST_START_DATE : 11-JUL-18 02.00.00.356202 AM UTC

RUN_COUNT       : 73

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

SQL> select program_action

from dba_SCHEDULER_PROGRAMS

where program_name='PMO_DEFERRED_GIDX_MAINT'

/

PROGRAM_ACTION

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

dbms_part.cleanup_gidx_internal(

noop_okay_in => 1);

现在手工调用,消除索引的孤儿状态,查询即刻恢复到正常的状态:

SQL> exec dbms_part.cleanup_gidx_internal(noop_okay_in => 1);

PL/SQL procedure successfully completed.

SQL> select index_name, status,orphaned_entries

from dba_indexes where index_name='ENMOTECH_IDX1';

INDEX_NAME     STATUS   ORP

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

ENMOTECH_IDX1     VALID    NO

在 Oracle 故障处理和学习的过程中,归纳汇总,聚点滴于一体,就能逐步让我们的知识成为体系,四通八达,所以,走多远,钻多深,取决于我们自己

资源下载

关注公众号:数据和云(OraNews)回复关键字获取

2018DTCC , 数据库大会PPT

2017DTC,2017 DTC 大会 PPT

DBALIFE ,“DBA 的一天”海报

DBA04 ,DBA 手记4 电子书

122ARCH ,Oracle 12.2体系结构图

2017OOW ,Oracle OpenWorld 资料

PRELECTION ,大讲堂讲师课程资料

近期文章

仅仅使用AWR做报告? 性能优化还未入门

实战课堂:一则CPU 100%的故障分析

杨廷琨:如何编写高效SQL(含PPT)

一份高达555页的技术PPT会是什么样子?

大象起舞:用PostgreSQL解海盗分金问题

ProxySQL!像C罗一样的强大

高手过招:用SQL解决环环相扣刑侦推理问题

触类旁通:那些关于 TBL$OR$IDX$PART$NUM 的诡异案例和知识相关推荐

  1. 呐,你要的新年攻略和文章汇总

    红灯笼挂在墙上,吉祥春联贴在门上,多彩烟花映在头上,浓浓年味洋溢心上,合家欢乐围绕桌上,喜庆如意写在脸上,我们整理了这一年以来的精彩文章奉上."数据和云"祝大家新春快乐!感谢大家一 ...

  2. 盘点2018:数据和云最受欢迎的十大精品文章

    转眼又到了总结一年的时刻,这一年我们积累了很多,也分享了很多,但是我们的努力到底帮助了多少人,又获得了多少认同和赞赏,这份公道只能是各在人心.如果您觉得一下我们总结的列表,其中某一篇文章曾经帮助到你, ...

  3. 张孝祥整理Java就业面试题大全

    提示:本大全每半月更新一次,请持续保持关注!谢谢! 索取网址:www.itcast.cn 从享受生活的角度上来说:"程序员并不是一种最好的职业,我认为两种人可以做程序员,第一,你不做程序员, ...

  4. java 面试题详解(转自传智播客张孝祥老师)

    张孝祥正在整理Java就业面试题大全 - 每天进步一点点... ... - CSDN博客2011年11月24日 星期四 设为主页 加入收藏帮助 | 留言交流 | 登录 首 页 阅览室 馆友 我的图书馆 ...

  5. 传智播客 - java 就业面试题大全

      从享受生活的角度上来说:"程序员并不是一种最好的职业,我认为两种人可以做程序员,第一,你不做程序员,你就没有什么工作可做,或者说是即使有可以做的工作但是你非常不愿意去做:第二,你非常痴迷 ...

  6. 张孝祥整理java面试题

    从享受生活的角度上来说:"程序员并不是一种最好的职业,我认为两种人可以做程序员,第一,你不做程序员,你就没有什么工作可做,或者说是即使有可以做的工作但是你非常不愿意去做:第二,你非常痴迷和爱 ...

  7. Java就业面试题大全

    从享受生活的角度上来说:"程序员并不是一种最好的职业,我认为两种人可以做程序员,第一,你不做程序员,你就没有什么工作可做,或者说是即使有可以做的工作但是你非常不愿意去做:第二,你非常痴迷和爱 ...

  8. spark编程基础--5.4综合实例

    操作指令如下: cd /usr/local/hadoop./sbin/start-dfs.sh./bin/hdfs dfs -mkdir -p spark/mycode/rdd/TopN./bin/h ...

  9. 标量子查询产生的SQL性能瓶颈,该怎么合理优化?

    来自:DBAplus社群 作者介绍 郝昊喆,新炬网络数据库专家.擅长数据库方面的开发.整体架构及复杂SQL的调优,参与了多个行业核心系统的优化工作,目前专注于对开源技术.自动化运维和性能调优技术的研究 ...

最新文章

  1. ggThemeAssist:鼠标调整ggplot2主题,不用再记这些代码啦!
  2. 微信小程序 bindtap 绑定事件
  3. iOS之UIview动画
  4. jdbc mysql 远程数据库_jdbc 连接远程mysql数据库的有关问题
  5. Sbo通用数据选择功能的实现
  6. centos7 下安装Poco
  7. NB-IOT(5)---关于NB-IoT的十大问题和答案
  8. LINUX 硬盘分区及文件系统
  9. UVALive 3942 Trie加速dp
  10. Mongo之架构部署(Replica Sets+Sharding)
  11. 使用电脑小技巧70个
  12. Ice helloworld
  13. 基于Opencv的人脸&姓名&表情&年龄&种族&性别识别系统(源码&教程)
  14. Android平台下JNI调用第三方so库
  15. 给程序员的 2018 新年计划清单
  16. 计算机主板品牌排行榜,电脑主板十大品牌排行榜
  17. 信息传输速率与传信率_波特率和数据传输速率的关系
  18. U3D RPC网络传输相机画面
  19. 下载公司 svn 项目后启动报错之 uniapp no such file or direc
  20. 计算机网络学习笔记(详尽版)

热门文章

  1. raspberry pi3_Raspberry Pi 3,Linux Mint安全漏洞,针对Zika病毒的Google数据处理等
  2. 2019年南大计算机开放日_开放式硬件计算机年
  3. idea非开源安装指南_开发人员开源指南
  4. 荷兰牛栏 荷兰售价_荷兰研究小组授予的数据共享项目
  5. jQuery源码分析 Callbacks
  6. JavaScript中语句与函数的执行辨析
  7. HTML min/maxlength
  8. sort和qsort函数的用法
  9. python 魔法函数是什么意思_Python 中的魔法函数
  10. 1. K近邻算法(KNN)