背景:
接到用户报障说一套Oracle19c数据库近期出现insert语句执行变慢的情况,执行一次数据插入需要1秒+的时间,而且问题发生的数据库是跑在一体机上面,数据插入正常不应该这么慢,需要分析插入慢的原因
问题:
数据库近期出现insert语句执行变慢的情况,执行一次数据插入需要1秒+的时间,查看问题的insert语句使用了大量的绑定变量,数量达到5000+个
--语句通过union构造多条数据进行批量插入
insert into table(A,B,C)
select :1,:2,:3
from dual
uion all
select :4,:5,:6
from dual
...
uion all
select :5098,:5099,:5100
from dual
问题分析:
查看语句的执行信息,可以看到语句下面有多个子游标,执行速度时快时慢,有的游标执行速度最慢到达3-4秒,最快则0.1秒,每个游标的执行计划都是相同的4069240402
我们首先排除是由于执行计划慢导致的问题一是语句的执行计划非常简单直接,就是通过查询dual然后插入表,二是语句的执行计划固定都是4069240402,但不同游标的执行速度会不一样
这里语句比较有问题的是,语句的有大量的子游标,游标复用率极低,基本每次执行都新生成一个子游标
继续通过10046跟踪语句的执行消耗,可以看到语句的主要消耗在CPU的execute,语句Misses in libarary cache during execute次数等于执行次数,每次执行在共享池里面均没有复用的缓存游标,也就是说每次执行都需要重新解析,结合语句存在大量的子游标,我们基本可以确定语句的主要性能瓶颈在于语句的子游标复用率极低,每次执行都需要重新硬解析生成新的子游标
查看语句的游标使用情况,通过数据库字典V$SQL_SHARED_CURSOR获取到语句下面有1000+的子游标,生成子游标的原因都是为BIND_MISMATCH
分析语句bind mismatch出现的原因,通过数据库字典v$sql_bind_capture查看应用SQL语句传入的绑定变量字段类型,再与插入目标表的字段类型进行比对,我们发现主要的原因为
1 传入变量类型TIMESTAMP与目标表的类型DATE不一致
2 传入变量varchar2长度跨度很大,同一个变量值,传入的长度出现分别为32,128,2000,4000,8192
根据Oracle官方对于bind mismatch的说明(Troubleshooting: High Version Count Issues (Doc ID 296377.1)),上述两种情形都会导致语句由于bind mismatch产生一个新的子游标,发生硬解析
优化建议:
1 确保应用传入SQL语句输入变量与插入的目标表一致,建议调整应用传入的变量类型为date,保持与目标表一致
2 通过event 10503调大会话级别的字符串bind buffer,以确保会话生成的游标buffer长度一致,建议将bind buffer调为4000(接近sql绑定变量的max_length)
event 10503测试:
--创建测试表
create table TESTBIND1
(
aaa VARCHAR2(4000),
bbb VARCHAR2(4000),
ccc VARCHAR2(4000)
);
--会话设置10503事件
ALTER SESSION SET EVENTS '10503 trace name context forever, level 4000';
--执行插入
DECLARE v_AAA VARCHAR2(3000):='A';
v_BBB VARCHAR2(10):='B';
v_CCC VARCHAR2(10):='C';
BEGIN
INSERT INTO TESTBIND1 VALUES(v_AAA,v_BBB,v_CCC);
COMMIT;
END;
/
--关闭10503事件
ALTER SESSION SET EVENTS '10503 trace name context off';
通过上述的SQL代码进行测试,可以看到会话设置了10503事件之后,即使客户端申明的字符变量长度小于4000,语句变量传入数据库时,绑定变量的长度会被固定为4000