Luk*_*der 5 sql oracle concurrency contention sql-merge
在Oracle 11gR2上,我最近遇到了一个非常有趣的情况,涉及阻塞(但是空闲!)MERGE语句,该语句挂起"来自客户端的SQL*Net消息"事件,导致后续的,并发执行的MERGE语句在第一个语句上阻塞" cursor:pin S等待X"事件.在Oracle Enterprise Manager中,可以观察到以下内容:
这种情况变得更加严重,因为上述Session-ID 1204无法被杀死:
alter system kill session 'sid,serial#';
alter system kill session 'sid,serial#' immediate;
Run Code Online (Sandbox Code Playgroud)
我们的DBA有时会破坏操作系统进程,但通常需要重新启动整个数据库.幸运的是,到目前为止,仅在测试系统上,从未投入生产.
我知道这可能是一个类似于这个相当含糊的问题中报告的类似问题:Oracle更新/插入卡住,DB CPU为100%,并发性高,来自客户端的SQL*Net等待消息.我还会再次报告,因为我有一个清晰的再现路径,我将作为答案报告.
当CLOB数据类型用作传递给MERGE语句ON子句的值时,这似乎是Oracle中的一个错误.假设这个数据库:
CREATE TABLE t (
v INT,
s VARCHAR2(400 CHAR)
);
Run Code Online (Sandbox Code Playgroud)
现在,在任何Oracle客户端中运行以下语句,包括SQL*Plus,SQL Developer或JDBC,这有助于非常轻松地重现问题(我正在使用Oracle 11g XE 11.2.0.2.0):
MERGE INTO t
USING (
SELECT
1 v,
CAST('abc' AS CLOB) s
FROM DUAL
) s
ON (t.s = s.s) -- Using a CLOB here causes the bug.
WHEN MATCHED THEN UPDATE SET
t.v = s.v
WHEN NOT MATCHED THEN INSERT (v, s)
VALUES (s.v, s.s);
Run Code Online (Sandbox Code Playgroud)
这个例子很愚蠢,CLOB这里被"意外"所束缚.尽管如此,这样的声明不应该在Oracle中创建一个僵尸会话,但它就在那里.我在SQL*Plus中运行上述语句三次然后运行这个...
SELECT
s.sid,
s.serial#,
s.sql_id,
s.event,
s.blocking_session,
q.sql_text
FROM v$session s
JOIN v$sql q
ON s.sql_id = q.sql_id
WHERE s.username = 'TEST'
AND UPPER(TRIM(q.sql_text)) LIKE 'MERGE%';
Run Code Online (Sandbox Code Playgroud)
......我明白了:
sid serial# sql_id event blocking_session
9 3 82a2k4sqzy1jq cursor: pin S wait on X 92
49 89 82a2k4sqzy1jq cursor: pin S wait on X 92
92 13 82a2k4sqzy1jq db file sequential read
Run Code Online (Sandbox Code Playgroud)
请注意报告的事件与原始事件("客户端的SQL*Net消息")之间的差异("db file sequential read "),它使用的是绑定变量
var v_s varchar2(50)
exec :v_s := 'abc'
MERGE INTO t
USING (
SELECT
1 v,
CAST(:v_s AS CLOB) s
FROM DUAL
) s
ON (t.s = s.s) -- Using a CLOB here causes the bug.
WHEN MATCHED THEN UPDATE SET
t.v = s.v
WHEN NOT MATCHED THEN INSERT (v, s)
VALUES (s.v, s.s);
Run Code Online (Sandbox Code Playgroud)
在SQL*Plus中运行的上述语句也会产生错误:
sid serial# sql_id event blocking_session
8 1 4w9zuxrumumgj SQL*Net message from client
90 7 4w9zuxrumumgj cursor: pin S wait on X 8
94 21 4w9zuxrumumgj cursor: pin S wait on X 8
Run Code Online (Sandbox Code Playgroud)
有趣的是,在以下PL/SQL语句中避免了该错误:
DECLARE
v_s CLOB := 'abc';
BEGIN
MERGE INTO t
USING (
SELECT
1 v,
CAST(v_s AS CLOB) s
FROM DUAL
) s
ON (t.s = s.s) -- Using a CLOB here causes the bug.
WHEN MATCHED THEN UPDATE SET
t.v = s.v
WHEN NOT MATCHED THEN INSERT (v, s)
VALUES (s.v, s.s);
END;
/
Run Code Online (Sandbox Code Playgroud)
我越来越:
CAST(v_s AS CLOB) s
*
ERROR at line 8:
ORA-06550: line 8, column 11:
PL/SQL: ORA-00932: inconsistent datatypes: expected - got CLOB
ORA-06550: line 4, column 7:
PL/SQL: SQL Statement ignored
Run Code Online (Sandbox Code Playgroud)
看起来好像PL/SQL引擎可以保护客户端免受此SQL引擎错误的影响.