[20250615]sqlplus大量赋值的问题.txt
[20250615]sqlplus大量赋值的问题.txt
--//前段时间测试大量绑定变量sq语句l执行"缓慢"的问题,实际上问题出在sqlplus客户端,通过pstack观察发现sqlplus要大量调用
--//lxoBinCmpMutl函数。当时猜测也许是sqlplus获得绑定变量定位方式"不合理"导致的情况,一直没时间验证这个问题,主要实际的情
--//况以及执行语句很少存在非常多绑定变量的情况。
1.环境:
SCOTT@book01p> @ ver2
==============================
PORT_STRING : x86_64/Linux 2.4.xx
VERSION : 21.0.0.0.0
BANNER : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
BANNER_FULL : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
BANNER_LEGACY : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
CON_ID : 0
PL/SQL procedure successfully completed.
2.建立测试脚本:
$ cat a.txt
set pagesize 0
set head off
set feedback off
set verify off
set timing off
spool b1.txt
select 'set termout off' from dual;
select 'variable b'||to_char(level)||' number;' from dual connect by level<=&1;
select 'begin' from dual;
select ':b'||to_char(level)||' :='|| to_char(level)||';' from dual connect by level<=&1;
select 'end;' from dual;
select '/' from dual ;
select 'set termout on' txt from dual;
spool off
spool b2.txt
--select 'set timing on' txt from dual;
select 'select count(data_object_id) from t where (1,object_id) in (' txt from dual ;
select '(1,:b'||to_char(level)||'),' txt from dual connect by level<=&&1 -1 ;
select '(1,:b'||to_char(&&1)||'));' txt from dual ;
--select 'set timing off' txt from dual;
spool off
set pagesize 9999
set head on
set feedback on
--//分别建立2个文件b1.txt赋值,b2.txt脚本执行。
$ cat lx.gdb
set pagination off
set logging overwrite on
set logging on
set $lx_count = 0
break lxoBinCmpMutl
commands
silent
printf "lxoBinCmpMutl count %02d -", ++$lx_count
c
end
--//set $lx_count = 0
3.测试:
--//session 1:
SCOTT@book01p> @ spid
==============================
SID : 142
SERIAL# : 40846
PROCESS : 3848
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SERVER : DEDICATED
SPID : 3850
PID : 49
P_SERIAL# : 7
KILL_COMMAND : alter system kill session '142,40846' immediate;
PL/SQL procedure successfully completed.
--//sqlplus进程号是3848。
--//window 1:
$ gdb -f -p 3848 -x lx.gdb
--//session 1:
SCOTT@book01p> variable c1 number
--//window 1:
Breakpoint 1 at 0x7f6edc932ec0
(gdb) c
Continuing.
lxoBinCmpMutl count 01
....
lxoBinCmpMutl count 47
--//可以发现简单的定义新的变量c1,调用lxoBinCmpMutl 47次。
--//按ctrl+c退出gdb,再次启动执行
$ gdb -f -p 3848 -x lx.gdb
--//session 1,再次定义新的变量c2:
SCOTT@book01p> variable c2 number
--//window 1:
Breakpoint 1 at 0x7f6edc932ec0
(gdb) c
Continuing.
lxoBinCmpMutl count 01
...
lxoBinCmpMutl count 47
lxoBinCmpMutl count 48
--//可以发现简单的定义新的变量c2,调用lxoBinCmpMutl 48次,比前面的variable c1 number多调用1次lxoBinCmpMutl。
--//可以推测定义变量越多调用lxoBinCmpMutl越多。赋值以及sqlplus执行sql语句也出现类似的情况,不再测试。
--//估计sqlplus探查定义变量的算法采用逐个探查的方式,如果类似存在hash链表之类的探查就不存在这个问题。
--//继续看后面的测试。
4.继续:
--//假设sqlplus会话存在许多绑定变量。
SCOTT@book01p> @ a.txt 65535
-//输出略。
--//编辑b1.txt,并且适当编辑满足前面begin..end之间可以定义32767个绑定变量的限制。
SCOTT@book01p> @ b1.txt
--//等待执行完成。
$ ps -efvp 4171
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
4171 pts/7 Ss+ 0:01 0 13 122270 16460 0.2 /u01/app/oracle/product/21.0.0/dbhome_1/bin/sqlplus
$ ps -efvp 4171
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
4171 pts/7 Rs+ 2:05 0 13 153938 46240 0.5 /u01/app/oracle/product/21.0.0/dbhome_1/bin/sqlplus
--//执行过程可以发现DRS,RSS不断增加。
$ ps -efvp 4171
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
4171 pts/7 Ss+ 4:23 0 13 156906 50476 0.6 /u01/app/oracle/product/21.0.0/dbhome_1/bin/sqlplus
画一个表格:
---------------------------------------------------
执行语句 gdb最后显示
---------------------------------------------------
select :b1 from dual lxoBinCmpMutl count 5022
select :b2 from dual lxoBinCmpMutl count 5023
select :b3 from dual lxoBinCmpMutl count 5024
select :b4 from dual lxoBinCmpMutl count 5025
...
select :b9 from dual lxoBinCmpMutl count 5030
--//每次+1.5030-5022=8
select :b10 from dual lxoBinCmpMutl count 5022 --//与前面:b1测试一样。
select :b11 from dual lxoBinCmpMutl count 5023 --//与前面:b2测试一样。
select :b12 from dual lxoBinCmpMutl count 5024 --//与前面:b3测试一样。
...
select :b99 from dual lxoBinCmpMutl count 5111
--//每次+1.5111-5022 = 89
--//测试到这里,实际上可以猜测sqlplus按照变量的长度建立1个链表,根据定义顺序存放。或者按照变量的长度分类。
select :b100 from dual lxoBinCmpMutl count 5022 --//验证自己的判断。
select :b101 from dual lxoBinCmpMutl count 5023
...
select :b300 from dual lxoBinCmpMutl count 5222
select :b300 from dual lxoBinCmpMutl count 5322
...
select :b999 from dual lxoBinCmpMutl count 5921
--//每次+1. 5921-5022 = 899
select :b1000 from dual lxoBinCmpMutl count 5022 --//验证自己的判断。
...
select :b2000 from dual lxoBinCmpMutl count 6022
...
select :b9999 from dual lxoBinCmpMutl count 14021
--//每次+1. 14021-5022 = 8999
select :b10000 from dual lxoBinCmpMutl count 5023 --//这里为什么比前面多1,是因为登录时我的环境定义了变量MY_SID,长度为6.
...
select :b65535 from dual lxoBinCmpMutl count 60558
--//每次+1. 60558-5023 = 55535
select :b0 from dual lxoBinCmpMutl count 56 --//该变量不存在,按照理解应该至少调用lxoBinCmpMutl 5030次。5030-56= 4974
select :a01 from dual lxoBinCmpMutl count 137 --//该变量不存在,按照理解应该至少调用lxoBinCmpMutl 5111次。5111-137 = 4974
select :b65536 from dual lxoBinCmpMutl count 55584 --//该变量不存在,按照理解应该至少调用lxoBinCmpMutl 60558次。60558-55584 = 4974
--//gdb可以不需要退出,按ctrl+c中断后,执行set $lx_count=0,可以重新计数。
select :b1,:b1 from dual lxoBinCmpMutl count 9996
select :b1,:b2 from dual lxoBinCmpMutl count 9998
select :b65535,b65534 from dual lxoBinCmpMutl count 121068
--//看看执行b2.txt的情况:
SCOTT@book01p> @ sl all
alter session set statistics_level = all;
Session altered.
SCOTT@book01p> set timing on
SCOTT@book01p> @ b2.txt
COUNT(DATA_OBJECT_ID)
---------------------
3970
1 row selected.
Elapsed: 00:04:53.35
SCOTT@book01p> @ b2.txt
COUNT(DATA_OBJECT_ID)
---------------------
3970
1 row selected.
Elapsed: 00:04:47.79
SCOTT@book01p> @ dpc '' '-peeked_binds -projection' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID 97pbm7taxr6d8, child number 1
-------------------------------------
select count(data_object_id) from t where (1,object_id) in ( (1,:b1),
(1,:b2), (1,:b3), (1,:b4), (1,:b5), (1,:b6), (1,:b7), (1,:b8), (1,:b9),
(1,:b10), (1,:b11), (1,:b12), (1,:b13), (1,:b14), (1,:b15), (1,:b16),
(1,:b17), (1,:b18), (1,:b19), (1,:b20), (1,:b21), (1,:b22), (1,:b23),
(1,:b24), (1,:b25), (1,:b26), (1,:b27), (1,:b28), (1,:b29), (1,:b30),
(1,:b31), (1,:b32), (1,:b33), (1,:b34), (1,:b35), (1,:b36), (1,:b37),
(1,:b38), (1,:b39), (1,:b40), (1,:b41), (1,:b42), (1,:b43), (1,:b44),
(1,:b45), (1,:b46), (1,:b47), (1,:b48), (1,:b49), (1,:b50), (1,:b51),
(1,:b52), (1,:b53), (1,:b54), (1,:b55), (1,:b56), (1,:b57), (1,:b58),
(1,:b59), (1,:b60), (1,:b61), (1,:b62), (1,:b63), (1,:b64), (1,:b65),
(1,:b66), (1,:b67), (1,:b68), (1,:b69), (1,:b70), (1,:b71), (1,:b72),
(1,:b73), (1,:b74), (1,:b75), (1,:b76), (1,:b77), (1,:b78), (1,:b79),
(1,:b80), (1,:b81), (1,:b82), (1,:b83), (1,:b84), (1,:b85), (1,:b86),
(1,:b87), (1,:b88), (1,:b89), (1,:b90), (1,:b91), (1,:b92), (1,:b93),
(1,:b94), (1,:b95)
Plan hash value: 2966233522
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 1013 (100)| | 1 |00:00:00.03 | 1479 |
| 1 | SORT AGGREGATE | | 1 | 1 | 7 | | | 1 |00:00:00.03 | 1479 |
|* 2 | TABLE ACCESS FULL| T | 1 | 4808 | 33656 | 1013 (60)| 00:00:01 | 3970 |00:00:00.03 | 1479 |
---------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
2 - SEL$1 / "T"@"SEL$1"
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("DATA_OBJECT_ID" IS NOT NULL AND INTERNAL_FUNCTION("OBJECT_ID")))
39 rows selected.
Elapsed: 00:00:15.65
--//执行计划选择全表扫描,但是注意看A-Time=00:00:00.03,大部分时间花在sqlplus的客户端探查绑定变量值到sql语句上。
5.小结:
--//猜测sqlplus按照变量的长度建立1个链表,根据定义顺序存放或者按照变量的长度分类。
--//不清楚变量不存在的情况。
--//前段时间测试大量绑定变量sq语句l执行"缓慢"的问题,实际上问题出在sqlplus客户端,通过pstack观察发现sqlplus要大量调用
--//lxoBinCmpMutl函数。当时猜测也许是sqlplus获得绑定变量定位方式"不合理"导致的情况,一直没时间验证这个问题,主要实际的情
--//况以及执行语句很少存在非常多绑定变量的情况。
1.环境:
SCOTT@book01p> @ ver2
==============================
PORT_STRING : x86_64/Linux 2.4.xx
VERSION : 21.0.0.0.0
BANNER : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
BANNER_FULL : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
Version 21.3.0.0.0
BANNER_LEGACY : Oracle Database 21c Enterprise Edition Release 21.0.0.0.0 - Production
CON_ID : 0
PL/SQL procedure successfully completed.
2.建立测试脚本:
$ cat a.txt
set pagesize 0
set head off
set feedback off
set verify off
set timing off
spool b1.txt
select 'set termout off' from dual;
select 'variable b'||to_char(level)||' number;' from dual connect by level<=&1;
select 'begin' from dual;
select ':b'||to_char(level)||' :='|| to_char(level)||';' from dual connect by level<=&1;
select 'end;' from dual;
select '/' from dual ;
select 'set termout on' txt from dual;
spool off
spool b2.txt
--select 'set timing on' txt from dual;
select 'select count(data_object_id) from t where (1,object_id) in (' txt from dual ;
select '(1,:b'||to_char(level)||'),' txt from dual connect by level<=&&1 -1 ;
select '(1,:b'||to_char(&&1)||'));' txt from dual ;
--select 'set timing off' txt from dual;
spool off
set pagesize 9999
set head on
set feedback on
--//分别建立2个文件b1.txt赋值,b2.txt脚本执行。
$ cat lx.gdb
set pagination off
set logging overwrite on
set logging on
set $lx_count = 0
break lxoBinCmpMutl
commands
silent
printf "lxoBinCmpMutl count %02d -", ++$lx_count
c
end
--//set $lx_count = 0
3.测试:
--//session 1:
SCOTT@book01p> @ spid
==============================
SID : 142
SERIAL# : 40846
PROCESS : 3848
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SERVER : DEDICATED
SPID : 3850
PID : 49
P_SERIAL# : 7
KILL_COMMAND : alter system kill session '142,40846' immediate;
PL/SQL procedure successfully completed.
--//sqlplus进程号是3848。
--//window 1:
$ gdb -f -p 3848 -x lx.gdb
--//session 1:
SCOTT@book01p> variable c1 number
--//window 1:
Breakpoint 1 at 0x7f6edc932ec0
(gdb) c
Continuing.
lxoBinCmpMutl count 01
....
lxoBinCmpMutl count 47
--//可以发现简单的定义新的变量c1,调用lxoBinCmpMutl 47次。
--//按ctrl+c退出gdb,再次启动执行
$ gdb -f -p 3848 -x lx.gdb
--//session 1,再次定义新的变量c2:
SCOTT@book01p> variable c2 number
--//window 1:
Breakpoint 1 at 0x7f6edc932ec0
(gdb) c
Continuing.
lxoBinCmpMutl count 01
...
lxoBinCmpMutl count 47
lxoBinCmpMutl count 48
--//可以发现简单的定义新的变量c2,调用lxoBinCmpMutl 48次,比前面的variable c1 number多调用1次lxoBinCmpMutl。
--//可以推测定义变量越多调用lxoBinCmpMutl越多。赋值以及sqlplus执行sql语句也出现类似的情况,不再测试。
--//估计sqlplus探查定义变量的算法采用逐个探查的方式,如果类似存在hash链表之类的探查就不存在这个问题。
--//继续看后面的测试。
4.继续:
--//假设sqlplus会话存在许多绑定变量。
SCOTT@book01p> @ a.txt 65535
-//输出略。
--//编辑b1.txt,并且适当编辑满足前面begin..end之间可以定义32767个绑定变量的限制。
SCOTT@book01p> @ b1.txt
--//等待执行完成。
$ ps -efvp 4171
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
4171 pts/7 Ss+ 0:01 0 13 122270 16460 0.2 /u01/app/oracle/product/21.0.0/dbhome_1/bin/sqlplus
$ ps -efvp 4171
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
4171 pts/7 Rs+ 2:05 0 13 153938 46240 0.5 /u01/app/oracle/product/21.0.0/dbhome_1/bin/sqlplus
--//执行过程可以发现DRS,RSS不断增加。
$ ps -efvp 4171
PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND
4171 pts/7 Ss+ 4:23 0 13 156906 50476 0.6 /u01/app/oracle/product/21.0.0/dbhome_1/bin/sqlplus
画一个表格:
---------------------------------------------------
执行语句 gdb最后显示
---------------------------------------------------
select :b1 from dual lxoBinCmpMutl count 5022
select :b2 from dual lxoBinCmpMutl count 5023
select :b3 from dual lxoBinCmpMutl count 5024
select :b4 from dual lxoBinCmpMutl count 5025
...
select :b9 from dual lxoBinCmpMutl count 5030
--//每次+1.5030-5022=8
select :b10 from dual lxoBinCmpMutl count 5022 --//与前面:b1测试一样。
select :b11 from dual lxoBinCmpMutl count 5023 --//与前面:b2测试一样。
select :b12 from dual lxoBinCmpMutl count 5024 --//与前面:b3测试一样。
...
select :b99 from dual lxoBinCmpMutl count 5111
--//每次+1.5111-5022 = 89
--//测试到这里,实际上可以猜测sqlplus按照变量的长度建立1个链表,根据定义顺序存放。或者按照变量的长度分类。
select :b100 from dual lxoBinCmpMutl count 5022 --//验证自己的判断。
select :b101 from dual lxoBinCmpMutl count 5023
...
select :b300 from dual lxoBinCmpMutl count 5222
select :b300 from dual lxoBinCmpMutl count 5322
...
select :b999 from dual lxoBinCmpMutl count 5921
--//每次+1. 5921-5022 = 899
select :b1000 from dual lxoBinCmpMutl count 5022 --//验证自己的判断。
...
select :b2000 from dual lxoBinCmpMutl count 6022
...
select :b9999 from dual lxoBinCmpMutl count 14021
--//每次+1. 14021-5022 = 8999
select :b10000 from dual lxoBinCmpMutl count 5023 --//这里为什么比前面多1,是因为登录时我的环境定义了变量MY_SID,长度为6.
...
select :b65535 from dual lxoBinCmpMutl count 60558
--//每次+1. 60558-5023 = 55535
select :b0 from dual lxoBinCmpMutl count 56 --//该变量不存在,按照理解应该至少调用lxoBinCmpMutl 5030次。5030-56= 4974
select :a01 from dual lxoBinCmpMutl count 137 --//该变量不存在,按照理解应该至少调用lxoBinCmpMutl 5111次。5111-137 = 4974
select :b65536 from dual lxoBinCmpMutl count 55584 --//该变量不存在,按照理解应该至少调用lxoBinCmpMutl 60558次。60558-55584 = 4974
--//gdb可以不需要退出,按ctrl+c中断后,执行set $lx_count=0,可以重新计数。
select :b1,:b1 from dual lxoBinCmpMutl count 9996
select :b1,:b2 from dual lxoBinCmpMutl count 9998
select :b65535,b65534 from dual lxoBinCmpMutl count 121068
--//看看执行b2.txt的情况:
SCOTT@book01p> @ sl all
alter session set statistics_level = all;
Session altered.
SCOTT@book01p> set timing on
SCOTT@book01p> @ b2.txt
COUNT(DATA_OBJECT_ID)
---------------------
3970
1 row selected.
Elapsed: 00:04:53.35
SCOTT@book01p> @ b2.txt
COUNT(DATA_OBJECT_ID)
---------------------
3970
1 row selected.
Elapsed: 00:04:47.79
SCOTT@book01p> @ dpc '' '-peeked_binds -projection' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID 97pbm7taxr6d8, child number 1
-------------------------------------
select count(data_object_id) from t where (1,object_id) in ( (1,:b1),
(1,:b2), (1,:b3), (1,:b4), (1,:b5), (1,:b6), (1,:b7), (1,:b8), (1,:b9),
(1,:b10), (1,:b11), (1,:b12), (1,:b13), (1,:b14), (1,:b15), (1,:b16),
(1,:b17), (1,:b18), (1,:b19), (1,:b20), (1,:b21), (1,:b22), (1,:b23),
(1,:b24), (1,:b25), (1,:b26), (1,:b27), (1,:b28), (1,:b29), (1,:b30),
(1,:b31), (1,:b32), (1,:b33), (1,:b34), (1,:b35), (1,:b36), (1,:b37),
(1,:b38), (1,:b39), (1,:b40), (1,:b41), (1,:b42), (1,:b43), (1,:b44),
(1,:b45), (1,:b46), (1,:b47), (1,:b48), (1,:b49), (1,:b50), (1,:b51),
(1,:b52), (1,:b53), (1,:b54), (1,:b55), (1,:b56), (1,:b57), (1,:b58),
(1,:b59), (1,:b60), (1,:b61), (1,:b62), (1,:b63), (1,:b64), (1,:b65),
(1,:b66), (1,:b67), (1,:b68), (1,:b69), (1,:b70), (1,:b71), (1,:b72),
(1,:b73), (1,:b74), (1,:b75), (1,:b76), (1,:b77), (1,:b78), (1,:b79),
(1,:b80), (1,:b81), (1,:b82), (1,:b83), (1,:b84), (1,:b85), (1,:b86),
(1,:b87), (1,:b88), (1,:b89), (1,:b90), (1,:b91), (1,:b92), (1,:b93),
(1,:b94), (1,:b95)
Plan hash value: 2966233522
---------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers |
---------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 1013 (100)| | 1 |00:00:00.03 | 1479 |
| 1 | SORT AGGREGATE | | 1 | 1 | 7 | | | 1 |00:00:00.03 | 1479 |
|* 2 | TABLE ACCESS FULL| T | 1 | 4808 | 33656 | 1013 (60)| 00:00:01 | 3970 |00:00:00.03 | 1479 |
---------------------------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
2 - SEL$1 / "T"@"SEL$1"
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(("DATA_OBJECT_ID" IS NOT NULL AND INTERNAL_FUNCTION("OBJECT_ID")))
39 rows selected.
Elapsed: 00:00:15.65
--//执行计划选择全表扫描,但是注意看A-Time=00:00:00.03,大部分时间花在sqlplus的客户端探查绑定变量值到sql语句上。
5.小结:
--//猜测sqlplus按照变量的长度建立1个链表,根据定义顺序存放或者按照变量的长度分类。
--//不清楚变量不存在的情况。