外键没有索引,是否会锁表

RDBMS 19.0.0.0上测试。

在Oracle的concept的第三章,有这么一段话,供参考。

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/cncpt/indexes-and-index-organized-tables.html#GUID-D0C53854-D92E-49D0-822A-39F3E7573752

In general, consider creating an index on a column in any of the following situations:

  • The indexed columns are queried frequently and return a small percentage of the total number of rows in the table.

  • A referential integrity constraint exists on the indexed column or columns. The index is a means to avoid a full table lock that would otherwise be required if you update the parent table primary key, merge into the parent table, or delete from the parent table.

  • A unique key constraint will be placed on the table and you want to manually specify the index and all index options.

在TOM大师的书上,关于外键没有索引,是否会锁表,有以下解释

不需要对外键加索引的情况  --  Page 209 (246 / 732)
1 不会从父表中删除行
2 不会去更新父表的唯一键,主键值
3 不会从父表连接到子表。
如果满足上面的三个条件,则外键可以不需要索引。

以下测试

创建主表dept,子表emp

create table emp(emp_id number primary key, dept_id number);
create table dept(dept_id number primary key);
alter table emp add constraint emp_fk1 foreign key (dept_id) references dept(dept_id);

ZBB@test>create table emp(emp_id number primary key, dept_id number);

Table created.

ZBB@test>create table dept(dept_id number primary key);

Table created.

ZBB@test>alter table emp add constraint emp_fk1 foreign key (dept_id) references dept(dept_id);

Table altered.

ZBB@test>

-- 插入数据

insert into dept values(10);
insert into dept values(20);
insert into dept values(30);
insert into emp values(1,10);
insert into emp values(2,20);
insert into emp values(3,10);
commit;


ZBB@test>insert into dept values(10);

1 row created.

ZBB@test>insert into dept values(20);

1 row created.

ZBB@test>insert into dept values(30);

1 row created.

ZBB@test>insert into emp values(1,10);

1 row created.

ZBB@test>insert into emp values(2,20);

1 row created.

ZBB@test>insert into emp values(3,10);

1 row created.

ZBB@test>commit;

Commit complete.

ZBB@test>

-- 在会话1 中(会话sid是278),删除子表emp中的记录,但是不提交

ZBB@test>select sid from v$mystat where rownum=1;

       SID
----------
       278

ZBB@test>

ZBB@test>delete from emp where dept_id = 10;

2 rows deleted.

ZBB@test>

-- 在会话2 中(会话SID是8),从父表中删除一些与子表中所删除数据不相关的数据行(一直卡在这里)

ZBB@test>select sid from v$mystat where rownum=1;

       SID
----------
         8

ZBB@test>
ZBB@test>delete from dept where dept_id = 30;

-- 查看锁的情况,可以看到会话1(SID 278)删除子表,持有3级锁。3 - ROW_X (SX): Row Exclusive Table Lock 。会话2(sid为8)删除父表中的其他的行,一直在申请锁。但是没有获取到锁。0 - NONE: lock requested but not yet obtained

SYS@test>select object_id,session_id,locked_mode from v$locked_object where 
object_id in (select object_id from dba_objects where object_name='EMP' and owner='ZBB');  2  

 OBJECT_ID SESSION_ID LOCKED_MODE
---------- ---------- -----------
     74312	    8		0
     74312	  278		3

SYS@test>

-- 在子表emp的外键列上添加索引,接下来,在子表emp上删除部门id为10的记录(不提交),在父表dept上删除部门id为30的记录,均会正常删除。

create index emp_fk1 on emp(dept_id);
ZBB@test>create index emp_fk1 on emp(dept_id);

Index created.

ZBB@test>

ZBB@test>delete from emp where dept_id = 10;

2 rows deleted.

ZBB@test>
ZBB@test>delete from dept where dept_id = 30;

1 row deleted.

ZBB@test>

-- 再次查看锁的情况,两个会话,均获取到了3级锁

SYS@test>select object_id,session_id,locked_mode from v$locked_object where 
object_id in (select object_id from dba_objects where object_name='EMP' and owner='ZBB');  2  

 OBJECT_ID SESSION_ID LOCKED_MODE
---------- ---------- -----------
     74312	    8		3
     74312	  278		3

SYS@test>

-- 说明
在子表中的删除提交之前,父表中的删除操作是被挂起的。在子表的外键列上没有正常的B树索引,每当你尝试在子表中进行插入或删除操作时,就会在父表上加上表锁。这在子表中事务完成之前,阻止对父表的删除和更新操作。

参考文档,锁的类型
https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/V-LOCKED_OBJECT.html#GUID-3F9F26AA-197F-4D36-939E-FAF1EFD8C0DD

LOCKED_MODE

NUMBER

Lock mode. The numeric values for this column map to these text values for the lock modes for table locks:

  • 0 - NONE: lock requested but not yet obtained

  • 1 - NULL

  • 2 - ROWS_S (SS): Row Share Lock

  • 3 - ROW_X (SX): Row Exclusive Table Lock

  • 4 - SHARE (S): Share Table Lock

  • 5 - S/ROW-X (SSX): Share Row Exclusive Table Lock

  • 6 - Exclusive (X): Exclusive Table Lock

See Also: Oracle Database Concepts for more information about lock modes for table locks


参考文档,表锁
https://docs.oracle.com/en/database/oracle/oracle-database/19/cncpt/data-concurrency-and-consistency.html#GUID-B6B075B7-DE83-45EF-8A9E-1C1F71F72598

Table Locks (TM)

A table lock, also called a TM lock, is acquired by a transaction when a table is modified by an INSERT, UPDATE, DELETE, MERGE, SELECT with the FOR UPDATE clause, or LOCK TABLE statement.

DML operations require table locks to reserve DML access to the table on behalf of a transaction and to prevent DDL operations that would conflict with the transaction.

A table lock can be held in any of the following modes:

  • Row Share (RS)

    This lock, also called a subshare table lock (SS), indicates that the transaction holding the lock on the table has locked rows in the table and intends to update them. A row share lock is the least restrictive mode of table lock, offering the highest degree of concurrency for a table.

  • Row Exclusive Table Lock (RX)

    This lock, also called a subexclusive table lock (SX), generally indicates that the transaction holding the lock has updated table rows or issued SELECT ... FOR UPDATE. An SX lock allows other transactions to query, insert, update, delete, or lock rows concurrently in the same table. Therefore, SX locks allow multiple transactions to obtain simultaneous SX and subshare table locks for the same table.

  • Share Table Lock (S)

    A share table lock held by a transaction allows other transactions to query the table (without using SELECT ... FOR UPDATE), but updates are allowed only if a single transaction holds the share table lock. Because multiple transactions may hold a share table lock concurrently, holding this lock is not sufficient to ensure that a transaction can modify the table.

  • Share Row Exclusive Table Lock (SRX)

    This lock, also called a share-subexclusive table lock (SSX), is more restrictive than a share table lock. Only one transaction at a time can acquire an SSX lock on a given table. An SSX lock held by a transaction allows other transactions to query the table (except for SELECT ... FOR UPDATE) but not to update the table.

  • Exclusive Table Lock (X)

    This lock is the most restrictive, prohibiting other transactions from performing any type of DML statement or placing any type of lock on the table.

END

附加一个tom大师的脚本

-- Unindexed foreign key columns

set echo on

column columns         format a30 word_wrapped
column table_name      format a15 word_wrapped
column constraint_name format a15 word_wrapped

select table_name, constraint_name,
       cname1 || nvl2(cname2,','||cname2,null) ||
       nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) ||
       nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) ||
       nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null)
              columns
    from ( select b.table_name,
                  b.constraint_name,
                  max(decode( position, 1, column_name, null )) cname1,
                  max(decode( position, 2, column_name, null )) cname2,
                  max(decode( position, 3, column_name, null )) cname3,
                  max(decode( position, 4, column_name, null )) cname4,
                  max(decode( position, 5, column_name, null )) cname5,
                  max(decode( position, 6, column_name, null )) cname6,
                  max(decode( position, 7, column_name, null )) cname7,
                  max(decode( position, 8, column_name, null )) cname8,
                  count(*) col_cnt
             from (select substr(table_name,1,30) table_name,
                          substr(constraint_name,1,30) constraint_name,
                          substr(column_name,1,30) column_name,
                          position
                     from user_cons_columns ) a,
                  user_constraints b	
            where a.constraint_name = b.constraint_name
              and b.constraint_type = 'R'
            group by b.table_name, b.constraint_name
         ) cons
   where col_cnt > ALL
           ( select count(*)
               from user_ind_columns i,
                    user_indexes     ui
              where i.table_name = cons.table_name
                and i.column_name in (cname1, cname2, cname3, cname4,
                                      cname5, cname6, cname7, cname8 )
                and i.column_position <= cons.col_cnt
                and ui.table_name = i.table_name
                and ui.index_name = i.index_name                
                and ui.index_type IN ('NORMAL','NORMAL/REV')
              group by i.index_name
           )
/
发布了754 篇原创文章 · 获赞 31 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/xxzhaobb/article/details/102796020