一条hang住数据库的SQL的分析

时间:2021-06-05 21:46:49

2017年某一天某个系统接口人找到我说 有个存储过程跑不过去,尝试了几次均报错:

ORA-12801:并行查询服务器p001 ,instance xx中发出错误信号
ORA-04030:在尝试分配16328字节(xxxx)时进程内存不足

并有监控收到空间急增的短信告警(oracle根目录下产生几十GB的core文件),alertlog内容如下:

Errors in file /oracle/db/diag/rdbms/almdb/almdb1/trace/almdb1_ora_61429.trc  (incident=261961):
ORA-07445: 出现异常错误: 核心转储 [kxsGetFullSqlText()+74] [SIGSEGV] [ADDR:0x0] [PC:0x220C50C] [SI_KERNEL(general_protection)] []
ORA-07445: 出现异常错误: 核心转储 [_wordcopy_fwd_dest_aligned()+189] [SIGSEGV] [ADDR:0x7F5B36F83000] [PC:0x3787A89D1D] [Invalid permissions for mapped object] []
Incident details in: /oracle/db/diag/rdbms/almdb/almdb1/incident/incdir_261961/almdb1_ora_61429_i261961.trc
Use ADRCI or Support Workbench to package the incident.
See Note 411.1 at My Oracle Support for error and packaging details.
Exception [type: SIGSEGV, SI_KERNEL(general_protection)] [ADDR:0x0] [PC:0x4C45B18, ksxplstdmp()+424] [flags: 0x0, count: 3]
Exception [type: SIGSEGV, SI_KERNEL(general_protection)] [ADDR:0x0] [PC:0x2D7BEEF, kghstack_err()+113] [flags: 0x2, count: 4]
Exception [type: SIGSEGV, SI_KERNEL(general_protection)] [ADDR:0x0] [PC:0x2D7BEEF, kghstack_err()+113] [flags: 0x2, count: 4]
Exception [type: SIGSEGV, SI_KERNEL(general_protection)] [ADDR:0x0] [PC:0x2D7BEEF, kghstack_err()+113] [flags: 0x2, count: 4]
Exception [type: SIGSEGV, SI_KERNEL(general_protection)] [ADDR:0x0] [PC:0x2D7BEEF, kghstack_err()+113] [flags: 0x2, count: 4]

引起报错的问题SQL已经定位到:

insert /*+ append */
into xxxxx partition(PS00000012840)
  (50多个列)
 select 50多个列
    from table(pack_lr.lr_f(pack_install.get_reporting_da te,
                            cursor (50多个列
                               from xxxxx_tmp
                              where (nominal != 0 or interest != 0))))

他们已经尝试过修改配置参数,报错依旧

ALTER SYSTEM SET pga_aggregate_target='25G' SCOPE=BOTH SID='almdb1';
ALTER SYSTEM SET _pga_max_size='6442450944' SCOPE=BOTH SID='almdb1';
ALTER SYSTEM SET shadow_core_dump='NONE' SCOPE=SPFILE SID='*';


------------------>开始分析

结合以上,所有可利用的信息只剩这条SQL。查看SQL和PLAN都没有问题,定位问题的关键便成了缩小报错的范围

1.查看是insert 还是select 端引起的问题

   select单拿出来跑,报错依旧。所以出问题的地方在select一侧【范围缩小】

2.sql的唯一条件nominal != 0 or interest != 0 通过尝试只保留一个条件,得出出问题的条件为interest != 0 【范围缩小】

------------------>开始分析SQL


3.sql语句本身不复杂,但是里面有个func。把func代码取出来(代码很复杂,只截取了一部分代码)

function lr_f(v_reporting_date date, the_cursor pack_lr.lr_in_cursor)
	return t_lr_out_table
	parallel_enable (
	partition the_cursor by hash(
		50多列
	)
)	order the_cursor by (
	50多列
)
pipelined
is ……
这个是他们项目买的一个产品,产品代码里面其实唯一可优化的点就是把hash,order by这种消耗pga的操作去掉,尝试了一下。报错依旧
4.查看SQL的执行计划。其实开始已经了解plan中没有太多有价值的信息。只因为刚刚在了解他们产品的时候得知 产品实现了一个功能<会自动把全表扫描定位到某个分区里面进行分区裁剪>查看执行计划是不是按照产品的原理实现了功能:发现执行计划里面只有table access full,而没有关于分区的PARTITION RANGE/LIST SINGLE字样。
到这里我们依然坚信是数据量引起的性能问题,单个分区和全表的数据量更是相差巨大。所以果断修改代码,让其只扫描单个分区。报错依旧

 
------------------>迭代分析


5.对比以往:性能分析的常用手段

今天和昨天(正常),全方位对比,唯一的区别就是多了20w条(总数600w条)数据。难道这20w就是代码处理的瓶颈?
为了验证我的猜想,我做了一个改写,将这个表按照某一个机构代码字段分成多个表,然后逐个去循环。代码一次性处理的数据量从600w行降到30w行,
这样不仅解决数据量的问题,更是从分表的角度对代码进行了优化。然后奇迹并没有出现,在循环到一张小表的时候程序又一次报错
到这里我们基本上可以排除是数据量的问题,可能是数据本身出现问题

6.紧接着通过表中的几个维度,将有问题的数据缩小至10000行




------------------>数据问题分析


7.时间字段最可能出现问题,首先查看时间字段有没有1900年之前的数据

8.查询每个字段的max(length),验证有没有字段超长的情况,超游标存储的情况
时间和字符类型查询均没有问题,现在只剩下12个number类型的字段,而且interest != 0也是number类型
其实number类型很少出现错误,要从这10000行里面一条一条排查,工作量可想而知。其实可以放弃了 
但是不甘心,想想已经坚持了这么久……
因为SQL语句中的条件是interest != 0,所以大胆的猜测一下,就是这个字段,引起的,所以在排查的时候,优先只看这个字段
终于  奇迹出现了。。。。。
一条hang住数据库的SQL的分析

所谓的奇迹,就是图中这个奇怪的现象


------------------>里面存了什么值


9.赶紧设置plsql dev 里面的preferances 勾选number fields to char 跑SQL   并在SQL*plus里面跑SQL------------------------->得出的结果都是 空白


------------------>为什么空白会显示成0


10.赶紧查一下SQL 条件interest is null  or interest ='' 都没有结果出来。所以interest字段里面存的值既不是0 也不是 空 ,难道是一个无限接近0的数?把这部分数过滤掉,是不是程序就能跑过去


------------------>解决问题


11.由以上分析 interest !=0 条件换成interest >1 or interest <-1 程序顺利通过--------->为了减少程序差异,最终改成interest >0.1 or interest <0报错完美解决



------------------>探究一下里面究竟存了什么值
于是做了以下两个实验
实验1:

一条hang住数据库的SQL的分析

实验2:

一条hang住数据库的SQL的分析