外键列上缺少索引会带来两个问题,限制并发性、影响性能。而这两个问题中的任意一个都可能会造成严重性能问题。
 
无论是Oracle的官方文档,还是在Tom的书中都说明了两种情况下可以忽略外键上的索引。其实我认为不需要那么麻烦,与增加一个
 
索引所带来的性能开销和磁盘空间开销相比,缺失索引可能引发的问题要严重得多。因此,我会选择在所有的外键列上添加索引,虽然
 
可能导致创建了部分多余的索引,但是这样相除了外键约束由于确实索引所带来的性能问题和并发性问题。
 
如果外键列上缺少索引,从主表关联子表的查询就只能对子表选择全表扫描的查询,这是显而易见的问题:
 
SQL> CREATE TABLE T_P (ID NUMBER, NAME VARCHAR2(30));
表已创建。
 
SQL> ALTER TABLE T_P ADD PRIMARY KEY (ID);
表已更改。
 
SQL> CREATE TABLE T_C (ID NUMBER, FID NUMBER, NAME VARCHAR2(30));
表已创建。
 
SQL> ALTER TABLE T_C ADD CONSTRAINT FK_T_C FOREIGN KEY (FID) REFERENCES T_P (ID);
表已更改。
 
SQL> INSERT INTO T_P SELECT ROWNUM, TABLE_NAME FROM ALL_TABLES;
已创建884行。
 
SQL> INSERT INTO T_C SELECT ROWNUM, MOD(ROWNUM, 884) + 1, OBJECT_NAME FROM ALL_OBJECTS;
已创建30339行。
 
SQL> COMMIT;
提交完成。
 
SQL> SELECT A.ID, A.NAME, B.NAME
2 FROM T_P A, T_C B
3 WHERE A.ID = B.FID
4 AND A.ID = 880;
 
ID         NAME                           NAME
---------- ------------------------------ ------------------------------
880        T_COMPRESS                     /eb2b6b5_Options1
880        T_COMPRESS                     DATE
880        T_COMPRESS                     DEF$_SCHEDULE
880        T_COMPRESS                     GV_$SESSION_EVENT
...
 
已选择34行。
 
执行计划
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 MERGE JOIN
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'T_P'
3 2 INDEX (UNIQUE SCAN) OF 'SYS_C002964' (UNIQUE)
4 1 FILTER
5 4 TABLE ACCESS (FULL) OF 'T_C'
 
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
190 consistent gets
0 physical reads
0 redo size
1829 bytes sent via SQL*Net to client
394 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
34 rows processed
 
由于缺少索引,上面的这个关联查询只能采用MERGE JOIN,而如果联立了外键列上的索引:
 
SQL> CREATE INDEX IND_T_C_FID ON T_C (FID);
索引已创建。
 
SQL> SELECT A.ID, A.NAME, B.NAME
2 FROM T_P A, T_C B
3 WHERE A.ID = B.FID
4 AND A.ID = 880;
 
ID         NAME                           NAME
---------- ------------------------------ ------------------------------
880        T_COMPRESS                     /e1538703_EntryInfoImpl
880        T_COMPRESS                     /7b832daf_ObjectStreamClassCom
880        T_COMPRESS                     java/awt/peer/ScrollbarPeer
880        T_COMPRESS                     /1982bd95_PermissionsEnumerato
...
 
已选择34行。
 
执行计划
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 1 TABLE ACCESS (BY INDEX ROWID) OF 'T_P'
3 2 INDEX (UNIQUE SCAN) OF 'SYS_C002964' (UNIQUE)
4 1 TABLE ACCESS (BY INDEX ROWID) OF 'T_C'
5 4 INDEX (RANGE SCAN) OF 'IND_T_C_FID' (NON-UNIQUE)
 
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
42 consistent gets
1 physical reads
0 redo size
1829 bytes sent via SQL*Net to client
394 bytes received via SQL*Net from client
4 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
34 rows processed
 
上面是影响性能的例子,下面看看外键列索引对并发性的影响:
 
SQL> SET AUTOT OFF
SQL> SELECT * FROM T_P WHERE ID < 5;
ID         NAME
---------- ------------------------------
1          SEG$
2          CLU$
3          OBJ$
4          FILE$
 
SQL> SELECT * FROM T_C WHERE ID < 5;
ID         FID        NAME
---------- ---------- ------------------------------
1          2          /1005bd30_LnkdConstant
2          3          /10076b23_OraCustomDatumClosur
3          4          /10297c91_SAXAttrList
4          5          /103a2e73_DefaultEditorKitEndP
 
下面在另一个会话中删除子表的一条记录:
 
SQL> SET SQLP 'SQL2> '
SQL2> DELETE T_C WHERE ID = 2;
已删除 1 行。
 
删除了一条为2的子表级联,其对应的主表记录ID为3,下面尝试在第一个会话新增一条ID为1000的记录,然后删除这条记录:
 
SQL> INSERT INTO T_P VALUES (1000, 'A');
已创建 1 行。
 
SQL> DELETE T_P WHERE ID = 1000;
已删除 1 行。
 
SQL> ROLLBACK;
回退已完成。
 
可以看到,并没有发生锁表的情况,这是因为子表外键列上有索引,删除主表的记录时,只会锁定子表参考主表的对应记录。
 
会话二回滚:
 
SQL2> ROLLBACK;
回退已完成。
 
下面删除外键索引:
SQL> DROP INDEX IND_T_C_FID;
索引已删除。
 
重复刚才的操作,在另一个会话执行删除操作:
SQL2> DELETE T_C WHERE ID = 2;
已删除 1 行。
 
在会话一重复插入和删除操作:
SQL> INSERT INTO T_P VALUES (1000, 'A');
已创建 1 行。
 
SQL> DELETE T_P WHERE ID = 1000;
这时会话被锁住,因为缺少了外键索引后,主表删除或更新记录会导致子表整个表被锁,而这会导致严重的系统并发问题。
 
SQL2> ROLLBACK;
回退已完成。
 
会话2回滚后,会话1的删除操作才可以继续执行:
已删除 1 行。
 
可能有些人会认为,系统中不存在删除而不会导致这个问题,其实不仅是删除,主键列的更新同样可以导致这个问题。
 
而且这种更新可能是工具帮你自动完成的,因为很多工具会自动生成SQL语句,而在这种生成的SQL语句中,UPDATE的列是表中的所有

列,所以即使主键的值没有发生变化,但是仍然是被更新了:
 
SQL2> DELETE T_C WHERE ID = 2;
已删除 1 行。
 
还是删除这条ID为2的子表记录,下面在主表执行一个更新操作:
SQL> UPDATE T_P SET ID = 500 WHERE ID = 500;
 
可以看到,不管值是否发生了变化,只要主键列被更新,就会导致操作被锁定。

显而易见,不加索引的外键列会造成严重的性能问题,所以除非你有十分的把握,否则还是在外键列上添加索引吧。

设置级关更新/删除

Oracle 级联删除使用格参考式:
CREATE TABLE A001(ID INT PRIMAY KEY,NAME VARCHAR2(20))  
CREATE TABLE A002(ID INT REFERENCES A001(ID)ON DELETE CASCADE,AGE NUMBER(2,0))

建立这个表的时候用到ON DELETE CASCADE或ON UPDATE CASCADE。

参考文档:http://space.itpub.net/?uid-17203031-action-viewspace-itemid-701832

关于 Oracle外键列上是否需要索引问题?的更多相关文章

  1. SQL 数据库 学习 007 通过一个示例简单介绍什么是字段、属性、列、元组、记录、表、主键、外键 (上)

    SQL 数据库 学习 007 通过一个示例简单介绍什么是字段.属性.列.元组.记录.表.主键.外键 (上) 我们来介绍一下:数据库是如何存储数据的. 数据库是如何存储数据的 来看一个小例子 scott ...

  2. Oracle外键不加索引会引起死锁问题

    转载链接:http://www.jb51.net/article/50161.htm 这篇文章主要介绍了Oracle外键不加索引引起死锁的情况及解决,需要的朋友可以参考下 --创建一个表,此表作为子表 ...

  3. Oracle外键(Foreign Key)使用详细的说明(一)

    Oracle外键(Foreign Key)使用详细的说明(一) 1.目标 演示如何Oracle使用外键数据库 2.什么是外键? 1)在Oracle数据库中,外键是用来实现參照完整性的方法之中的一个.打 ...

  4. 【转】PowerDesigner删除外键关系,而不删除外键列

    原文:https://blog.csdn.net/tomsyc/article/details/6075530 PowerDesigner中配置外键关系时,如果要删除配置的外键关系,默认设置会一同删除 ...

  5. PowerDesigner删除外键关系,而不删除外键列[转]

    PowerDesigner中配置外键关系时,如果要删除配置的外键关系,默认设置会一同删除外键列. 要更改此设置,需在菜单栏tools中打开Model Options,在Model Settings中点 ...

  6. PowerDesigner删除外键关系,而不删除外键列[转] 及编码格式

    PowerDesigner删除外键关系,而不删除外键列[转]  数据库 database  -> generate database ->format 设置为utf-8 PowerDesi ...

  7. ORACLE外键和锁

    在oracle中,如果外键未加索引,对父表的修改,会导致子表被加上全表锁.这包括两种情况: 1.删除父表中的行,如果外键上没有索引,会导致子表被加上全表锁 2.更新父表的主键(根据关系数据库的原则,更 ...

  8. Oracle外键需要建索引吗?

    关于Oracle中的外键,首先要说明一下. 1. 除非已定义了父表主键或唯一键约束,否则oracle将不允许创建子表的外键约束. 2. 在定义外键约束时,oracle不会自动创建索引,所以必须手动在与 ...

  9. 再一次强调,ORACLE外键必须加索引

    外键加索引是常识,必须牢记.本来不想写这样的简单案例.可是连续遇到好几起外键不加索引导致性能问题,所以还是写一下. 一个兄弟问我 delete from Sa_Sales_Comm_Detail s  ...

随机推荐

  1. delphi 文件夹权限设置(执行一个小脚本的笨办法)

    如题,研究了一天,也没再网上找到比较好的方式,自己做了一个.方法如下: 1.创建一个 cmd 命令文件.2.调用该命令. 代码如下:   S:='echo y|cacls h: /t /c /g ev ...

  2. perl5 第九章 关联数组/哈希表

    第九章 关联数组/哈希表 by flamephoenix 一.数组变量的限制二.定义三.访问关联数组的元素四.增加元素五.创建关联数组六.从数组变量复制到关联数组七.元素的增删八.列出数组的索引和值九 ...

  3. 如何使用 Barracuda 防火墙设置/保护 Azure 应用程序

     如果某企业在 Windows Azure 上托管某个应用程序,该应用程序会在某个特定时间暴露到 Internet,以用于商业用途.公共 Internet 带来 客户的同时也带来了攻击者. Tim ...

  4. qwebkit - Open new window after click using QT - Stack Overflow

    qwebkit - Open new window after click using QT - Stack Overflow Open new window after click using QT

  5. iOS9 白名单问题 -canOpenURL: failed for URL: "xx" - error:"This app is not allowed to query for scheme xx"

    [iOS开发]-canOpenURL: failed for URL: "xx" - error:"This app is not allowed to query fo ...

  6. 高可用mysql集群搭建

    对web系统来说,瓶颈大多在数据库和磁盘IO上面,而不是服务器的计算能力.对于系统伸缩性我们一般有2种解决方案,scale-up(纵向扩展)和scale-out(横向扩展).前者如扩内存,增加单机性能 ...

  7. java 解析国密SM2算法证书

    首先说明用Java自带的解析x509证书类,是不能解析sm2算法的证书,执行会抛出异常. 用开源库bouncycastle能够解析.详细代码 private byte[] getCSPK(byte[] ...

  8. IOS文件沙盒

    在进行IPhone开发的时候,常常需要将简单的数据保存到文件中,以便下次再重新打开的时候能够读取文件中保存的数据. 下面就来做一个简单的demo: 步骤: 1.创建一个SingleView项目,带有x ...

  9. Bug驱动开发(Bug-driven development)

    说实话,作为一个Domino开发者,像測试驱动开发(Test-driven development).功能驱动开发(Feature-driven development)之类软件开发的高大上的方法论( ...

  10. asp.net使用unescape读取js escape编码过的字符串

    escape() 是JavaScript的编码函数 例子:var esstring=escape("helloworld"); 为了防止数据传输读取中出现乱码现象,字符串往往要用J ...