创建测试环境:
[oracle@yang ~]$ sqlplus ‘/as sysdba’

SQL*Plus: Release 10.2.0.4.0 – Production on Thu Oct 27 00:03:46 2011

Copyright (c) 1982, 2007, Oracle. All Rights Reserved.

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 – Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> conn liu/liu
Connected.
SQL> create table b as select * from dba_objects;

Table created.

SQL> create table a as select * from dba_objects;

Table created.

SQL> alter table a add constraint a_pk primary key (OBJECT_ID);

Table altered.

SQL> alter table b add constraint b_fk foreign key (OBJECT_ID) references a (OBJECT_ID);

Table altered.

SQL>

session 1 执行:
SQL> delete from b where object_id=10000;

1 row deleted.

SQL> delete from a where object_id=10000;

1 row deleted.

session 2 执行:

s[oracle@yang ~]$ sqlplus ‘/as sysdba’

SQL*Plus: Release 10.2.0.4.0 – Production on Thu Oct 27 00:03:46 2011

Copyright (c) 1982, 2007, Oracle. All Rights Reserved.

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 – Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> conn liu/liu
Connected.
SQL> delete from b where object_id=10001;

1 row deleted.

SQL> delete from a where object_id=10001;

session 2被hang住;

通过以下SQL查询:

SELECT LK.SID,
SE.USERNAME,
SE.OSUSER,
SE.MACHINE,
DECODE(LK.TYPE,
‘TX’,
‘Transaction’,
‘TM’,
‘DML’,
‘UL’,
‘PL/SQL User Lock’,
LK.TYPE) LOCK_TYPE,
DECODE(LK.LMODE,
0,
‘None’,
1,
‘Null’,
2,
‘Row-S (SS)’,
3,
‘Row-X (SX)’,
4,
‘Share’,
5,
‘S/Row-X (SSX)’,
6,
‘Exclusive’,
TO_CHAR(LK.LMODE)) MODE_HELD,
DECODE(LK.REQUEST,
0,
‘None’,
1,
‘Null’,
2,
‘Row-S (SS)’,
3,
‘Row-X (SX)’,
4,
‘Share’,
5,
‘S/Row-X (SSX)’,
6,
‘Exclusive’,
TO_CHAR(LK.REQUEST)) MODE_REQUESTED,
TO_CHAR(LK.ID1) LOCK_ID1,
TO_CHAR(LK.ID2) LOCK_ID2,
OB.OWNER,
OB.OBJECT_TYPE,
OB.OBJECT_NAME,
LK.BLOCK,
SE.LOCKWAIT
FROM V$LOCK LK, DBA_OBJECTS OB, V$SESSION SE
WHERE LK.TYPE IN (‘TM’, ‘UL’)
AND LK.SID = SE.SID
AND LK.ID1 = OB.OBJECT_ID(+);

141 LIU oracle yang DML Row-S (SS) None 52153 0 LIU TABLE A 0 2F9C69BC
144 LIU oracle yang DML Row-X (SX) None 52153 0 LIU TABLE A 0
141 LIU oracle yang DML Row-X (SX) S/Row-X (SSX) 52152 0 LIU TABLE B 0 2F9C69BC
144 LIU oracle yang DML Row-X (SX) None 52152 0 LIU TABLE B 1

可以发现session 141 请求 SSX锁 而 SX与SSX不兼容 而被hang住

此时session 1:

SQL> delete from a where object_id=10002;

立刻产生死锁 因为session 1 需要短暂的获得b表的ssx锁 而此时b表正在被以sx锁持有 session1 hang死 而session 2在等待session 1持有b表的 sx锁释放 所以发生了死锁;

Deadlock graph:
———Blocker(s)——– ———Waiter(s)———
Resource Name process session holds waits process session holds waits
TM-0000cbb8-00000000 24 141 SX SSX 22 144 SX SSX
TM-0000cbb8-00000000 22 144 SX SSX 24 141 SX SSX
session 141: DID 0001-0018-00000006 session 144: DID 0001-0016-00000011
session 144: DID 0001-0016-00000011 session 141: DID 0001-0018-00000006

发现需要持有ssx锁

通常这种情况发生在外键没有建立index的情况下

oracle 建议外键必须加上index:
“You should almost always index foreign keys.
The only exception is when the matching unique or primary key is never updated or deleted.”

When a foreign key is unindexed, DML on the parent primary key results in a share row exclusive table lock
(or share-subexclusive table lock, SSX) on the child table, preventing DML from other transactions
against the child table. If the DML affects several rows in the parent table, the lock on the child table
is obtained and released immediately for each row in turn. Despite the speed of the lock-release process,
this can cause significant amounts of contention on the child table during periods of
heavy update/delete activity on the parent table.

When a foreign key is indexed, DML on the parent primary key results in a row share table lock
(or subshare table lock, SS) on the child table. This type of lock prevents other transactions
from issuing whole table locks on the child table, but does not block DML on either the parent or
the child table. Only the rows relating to the parent primary key are locked in the child table.

以下脚本可以帮助解决foreign key的问题:

=======
Script:
=======

———– cut ———————- cut ————– cut ————–

SET ECHO off
REM NAME: TFSFKCHLK.SQL
REM USAGE:”@path/tfsfkchk”
REM ————————————————————————–
REM REQUIREMENTS:
REM None — checks only the USER_ views
REM ————————————————————————–
REM This file checks the current users Foreign Keys to make sure of the
REM following:
REM
REM 1) All the FK columns are have indexes to prevent a possible locking
REM problem that can slow down the database.
REM
REM 2) Checks the ORDER OF THE INDEXED COLUMNS. To prevent the locking
REM problem the columns MUST be index in the same order as the FK is
REM defined.
REM
REM 3) If the script finds and miss match the script reports the correct
REM order of columns that need to be added to prevent the locking
REM problem.
REM
REM
REM
REM ————————————————————————-
REM Main text of script follows:

drop table ck_log;

create table ck_log (
LineNum number,
LineMsg varchar2(2000));

declare
t_CONSTRAINT_TYPE user_constraints.CONSTRAINT_TYPE%type;
t_CONSTRAINT_NAME USER_CONSTRAINTS.CONSTRAINT_NAME%type;
t_TABLE_NAME USER_CONSTRAINTS.TABLE_NAME%type;
t_R_CONSTRAINT_NAME USER_CONSTRAINTS.R_CONSTRAINT_NAME%type;
tt_CONSTRAINT_NAME USER_CONS_COLUMNS.CONSTRAINT_NAME%type;
tt_TABLE_NAME USER_CONS_COLUMNS.TABLE_NAME%type;
tt_COLUMN_NAME USER_CONS_COLUMNS.COLUMN_NAME%type;
tt_POSITION USER_CONS_COLUMNS.POSITION%type;
tt_Dummy number;
tt_dummyChar varchar2(2000);
l_Cons_Found_Flag VarChar2(1);
Err_TABLE_NAME USER_CONSTRAINTS.TABLE_NAME%type;
Err_COLUMN_NAME USER_CONS_COLUMNS.COLUMN_NAME%type;
Err_POSITION USER_CONS_COLUMNS.POSITION%type;

tLineNum number;

cursor UserTabs is
select table_name
from user_tables
order by table_name;

cursor TableCons is
select CONSTRAINT_TYPE,
CONSTRAINT_NAME,
R_CONSTRAINT_NAME
from user_constraints
where OWNER = USER
and table_name = t_Table_Name
and CONSTRAINT_TYPE = ‘R’
order by TABLE_NAME, CONSTRAINT_NAME;

cursor ConColumns is
select CONSTRAINT_NAME,
TABLE_NAME,
COLUMN_NAME,
POSITION
from user_cons_columns
where OWNER = USER
and CONSTRAINT_NAME = t_CONSTRAINT_NAME
order by POSITION;

cursor IndexColumns is
select TABLE_NAME,
COLUMN_NAME,
POSITION
from user_cons_columns
where OWNER = USER
and CONSTRAINT_NAME = t_CONSTRAINT_NAME
order by POSITION;

DebugLevel number := 99; — >>> 99 = dump all info`
DebugFlag varchar(1) := ‘N’; — Turn Debugging on
t_Error_Found varchar(1);

begin

tLineNum := 1000;
open UserTabs;
LOOP
Fetch UserTabs into t_TABLE_NAME;
t_Error_Found := ‘N’;
exit when UserTabs%NOTFOUND;

— Log current table
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, NULL );

tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Checking Table ‘||t_Table_Name);

l_Cons_Found_Flag := ‘N’;
open TableCons;
LOOP
FETCH TableCons INTO t_CONSTRAINT_TYPE,
t_CONSTRAINT_NAME,
t_R_CONSTRAINT_NAME;
exit when TableCons%NOTFOUND;

if ( DebugFlag = ‘Y’ and DebugLevel >= 99 )
then
begin
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Found CONSTRAINT_NAME = ‘|| t_CONSTRAINT_NAME);

tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Found CONSTRAINT_TYPE = ‘|| t_CONSTRAINT_TYPE);

tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Found R_CONSTRAINT_NAME = ‘|| t_R_CONSTRAINT_NAME);
commit;
end;
end if;

open ConColumns;
LOOP
FETCH ConColumns INTO
tt_CONSTRAINT_NAME,
tt_TABLE_NAME,
tt_COLUMN_NAME,
tt_POSITION;
exit when ConColumns%NOTFOUND;
if ( DebugFlag = ‘Y’ and DebugLevel >= 99 )
then
begin
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, NULL );

tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Found CONSTRAINT_NAME = ‘|| tt_CONSTRAINT_NAME);

tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Found TABLE_NAME = ‘|| tt_TABLE_NAME);

tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Found COLUMN_NAME = ‘|| tt_COLUMN_NAME);

tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Found POSITION = ‘|| tt_POSITION);
commit;
end;
end if;

begin
select 1 into tt_Dummy
from user_ind_columns
where TABLE_NAME = tt_TABLE_NAME
and COLUMN_NAME = tt_COLUMN_NAME
and COLUMN_POSITION = tt_POSITION;

if ( DebugFlag = ‘Y’ and DebugLevel >= 99 )
then
begin
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Row Has matching Index’ );
end;
end if;
exception
when Too_Many_Rows then
if ( DebugFlag = ‘Y’ and DebugLevel >= 99 )
then
begin
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Row Has matching Index’ );
end;
end if;

when no_data_found then
if ( DebugFlag = ‘Y’ and DebugLevel >= 99 )
then
begin
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘NO MATCH FOUND’ );
commit;
end;
end if;

t_Error_Found := ‘Y’;

select distinct TABLE_NAME
into tt_dummyChar
from user_cons_columns
where OWNER = USER
and CONSTRAINT_NAME = t_R_CONSTRAINT_NAME;

tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum, ‘Changing data in table ‘||tt_dummyChar
||’ will lock table ‘ ||tt_TABLE_NAME);

commit;
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum,’Create an index on table ‘||tt_TABLE_NAME
||’ with the following columns to remove lock problem’);

open IndexColumns ;
loop
Fetch IndexColumns into Err_TABLE_NAME,
Err_COLUMN_NAME,
Err_POSITION;
exit when IndexColumns%NotFound;
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum,’Column = ‘||Err_COLUMN_NAME||’ (‘||Err_POSITION||’)’);
end loop;
close IndexColumns;
end;
end loop;
commit;
close ConColumns;
end loop;
if ( t_Error_Found = ‘N’ )
then
begin
tLineNum := tLineNum + 1;
insert into ck_log ( LineNum, LineMsg ) values
( tLineNum,’No foreign key errors found’);
end;
end if;
commit;
close TableCons;
end loop;
commit;
end;
/

select LineMsg
from ck_log
where LineMsg NOT LIKE ‘Checking%’ AND
LineMsg NOT LIKE ‘No foreign key%’
order by LineNum
/

通过以下脚本 可以查出没有被indexed的外键:

REM List foreign keys with no matching index on child table – causes locks

set linesize 150;

col owner for a20;
col COLUMN_NAME for a20;

SELECT c.owner, c.constraint_name, c.table_name, cc.column_name, c.status
FROM dba_constraints c, dba_cons_columns cc
WHERE c.constraint_type = ‘R’
AND c.owner NOT IN (‘SYS’,
‘SYSTEM’,
‘SYSMAN’,
‘EXFSYS’,
‘WMSYS’,
‘OLAPSYS’,
‘OUTLN’,
‘DBSNMP’,
‘ORDSYS’,
‘ORDPLUGINS’,
‘MDSYS’,
‘CTXSYS’,
‘AURORA$ORB$UNAUTHENTICATED’,
‘XDB’,
‘FLOWS_030000’,
‘FLOWS_FILES’)
AND c.owner = cc.owner
AND c.constraint_name = cc.constraint_name
AND NOT EXISTS
(SELECT ‘x’
FROM dba_ind_columns ic
WHERE cc.owner = ic.table_owner
AND cc.table_name = ic.table_name
AND cc.column_name = ic.column_name
AND cc.position = ic.column_position
AND NOT EXISTS
(SELECT owner, index_name
FROM dba_indexes i
WHERE i.table_owner = c.owner
AND i.index_Name = ic.index_name
AND i.owner = ic.index_owner
AND (i.status = ‘UNUSABLE’ OR
i.partitioned = ‘YES’ AND EXISTS
(SELECT ‘x’
FROM dba_ind_partitions ip
WHERE status = ‘UNUSABLE’
AND ip. index_owner = i. owner
AND ip. index_Name = i. index_name
UNION ALL
SELECT ‘x’
FROM dba_ind_subpartitions isp
WHERE status = ‘UNUSABLE’
AND isp. index_owner = i. owner
AND isp. index_Name = i. index_name))))
ORDER BY 1, 2

 

related posts:
dead lock引起的数据库hang死