我从不同的会话执行查询时导致死锁。
TAB1 (ID, TARGET, STATE, NEXT) AND ID is primary key
Column ID is the primary key.
SELECT *
FROM
TAB1 WHERE NEXT = (SELECT MIN(NEXT) FROM TAB1 WHERE TARGET=? AND STATE=?)
AND TARGET=? AND STATE=? FOR UPDATE
在Oracle跟踪文件中,我看到了声明:
DEADLOCK DETECTED
Current SQL statement for this session:
SELECT ID, TARGET, NEXT, STATE FROM TAB1
WHERE NEXT=(SELECT MIN(NEXT) FROM TAB1 WHERE (TARGET='$any') AND ( STATE = 0))
AND (TARGET='$any')
AND (STATE = 0) FOR UPDATE
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or from issuing incorrect ad-hoc SQL. The following
information may aid in determining the deadlock:
Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TX-00010012-0102905b 54 474 X 52 256 X
TX-000a0005-00a30961 52 256 X 54 474 X
session 474: DID 0001-0036-00000002 session 256: DID 0001-0034-00000002
session 256: DID 0001-0034-00000002 session 474: DID 0001-0036-00000002
Rows waited on:
Session 256: obj - rowid = 00013181 - AAATGBAAzAABtPTAAI
(dictionary objn - 78209, file - 51, block - 447443, slot - 8)
Session 474: obj - rowid = 00013181 - AAATGBAAzAABtPUAAJ
(dictionary objn - 78209, file - 51, block - 447444, slot - 9)
Information on the OTHER waiting sessions:
Session 256:
pid=52 serial=58842 audsid=43375302 user: 106/B2B_ISINTERNAL
O/S info: user: admwmt, term: spl099wmt04.compucom.local, ospid: , machine: spl099wmt04.compucom.local/10.16.0.41
program: JDBC Connect Client
Current SQL Statement:
SELECT ID, TARGET, NEXT, STATE FROM TAB1
WHERE NEXT=(SELECT MIN(NEXT) FROM TAB1 WHERE (TARGET='$any') AND ( STATE = 0))
AND (TARGET='$any')
AND (STATE = 0) FOR UPDATE
End of information on OTHER waiting sessions.
===================================================
有什么方法可以避免这种情况吗?重写查询或索引?
答案 0 :(得分:0)
我认为原因可能是您实际上使用FOR UPDATE子句选择了两次相同的表,一次在主查询中,一次在子查询中。
答案 1 :(得分:0)
<强>更新强>
不是试图准确猜测Oracle如何检索行并强制执行计划,而是使用其中一个可用的UPDATE FOR
locking features可能更容易。
NOWAIT
或SKIP LOCKED
应该可以解决问题。虽然使用NOWAIT
,您可能需要添加一些应用程序逻辑,以便在发生错误后重试。
由于存在绑定变量,因此同一SQL语句可能存在多个执行计划。这通常是件好事,例如考虑这样的查询:select * from tab where status = ?
。全表扫描最适合流行状态,索引扫描对于罕见状态更有效。但是,如果一个计划使用索引并且一个使用表,则相同的语句将以不同的顺序检索资源,从而可能导致死锁。
强制语句始终使用相同的计划将防止死锁。
首先,您需要确认我的多个执行计划的理论是否正确。在此查询中查找多行,特别是为相同的SQL_ID查找不同的plan_hash_value
。
select child_number, plan_hash_value, gv$sql.*
from gv$sql
where sql_text like '%NEXT=(SELECT%';
然后,强制语句始终使用相同的计划是一个问题。一种简单的方法是找到修复特定计划的大纲,并对两个语句使用相同的提示集。希望强制计划对于所有绑定变量集仍然运行良好。
select *
from table(dbms_xplan.display_cursor(
sql_id => '<SQL_ID from above>',
cursor_child_no => <child_number from above>,
format => '+outline')
);