In the pervious
post, we discussed how AI process does not create auto indexing in the
scenario where minimum or maximum of a column is required.
Another scenario when an AI is not
created is when we hit the issues associated with the missing index on a
foreign key constraint.
Deleting a record from the parent table
without index on the associate foreign key column of the child table, we hit a
number of issues including having to perform a full table scan on the child
tables and the associated locking problems.
To illustrate, we will first create a
parent table.
ai_demo@ATP21C> create
table demo035p
2 nologging
3 as
4 select rownum as id , a.*
5 from stage a
6 where rownum <= 10000;
Table created.
And then a larger child table, with no
index on the associated foreign key constraint.
ai_demo@ATP21C> create
table demo035c
2 nologging
3 as
4 select rownum as pid ,
5 mod(rownum,1000)+1000 as cid ,
6 a.*
7 from stage a, stage b, stage c
8 where rownum <= 10000000 ;
Table created.
ai_demo@ATP21C> alter
table demo035p
2 add constraint pk_demo035p
3 primary key(id);
Table altered.
ai_demo@ATP21C> alter
table demo035c
2 add constraint fk_demo035c
3 foreign key(cid)
4 references demo035p;
Table altered.
If we decide to delete rows from the
parent table
ai_demo@ATP21C> set
autotrace traceonly exp statistics
ai_demo@ATP21C> delete from demo035p where id = 55;
1 row deleted.
Execution Plan
----------------------------------------------------------
Plan hash value: 4215583649
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1 | 4 | 1 (0)| 00:00:01 |
| 1 | DELETE | DEMO035P | | | | |
|* 2 | INDEX UNIQUE SCAN| PK_DEMO035P | 1 | 4 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------
Predicate Information
(identified by operation id):
---------------------------------------------------
2 - access("ID"=55)
Note
-----
- automatic DOP: Computed Degree of Parallelism is 1 because of no expensive parallel operation
- PDML disabled because child referential constraints are present
Statistics
----------------------------------------------------------
55 recursive calls
12 db block gets
164019 consistent gets
6 physical reads
2596 redo size
340 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
1 rows processed
We notice that even thought we delete one
row from the parent table, we perform a large number of consistent gets due to
a necessary full table scan on the child table, as Oracle is forced to check
the table for any possible FK violations. Without an index on child table
foreign key column, Oracle has no choice but to perform the relative expensive
full table scan.
Additionally, if we have an existing
transaction on the child table
ai_demo@ATP21C> @sid
INST_ SESSION_ID
----- ----------
1 54092
ai_demo@ATP21C> insert
into demo035c(pid,cid) values(10000001,1055);
1 row created.
And then in another session attempt to
delete a row from the parent table (from a different session)
ai_demo@ATP21C> @sid
INST_ SESSION_ID
----- ----------
1 12038
ai_demo@ATP21C> delete
from demo035p where id = 56;
that delete statement goes into a block
state, due to uncommitted transaction in the child table, which can lead to further
locking issues in other sessions.
ai_demo@ATP21C> select
l.object_id,u.object_name,l.session_id,l.locked_mode
2 from v$locked_object l,
3 user_objects u
4 where l.object_id = u.object_id;
OBJECT_ID OBJECT_NAM SESSION_ID LOCKED_MODE
---------- ---------- ---------- -----------
136548 DEMO035P 12038 3
136548 DEMO035P 54092 3
136549 DEMO035C 54092 3
ai_demo@ATP21C> select
sid,blocking_session_status, blocking_session,event
2 from gv$session
3 where sid = 12038;
SID BLOCKING_SE BLOCKING_SESSION EVENT
---------- ----------- ---------------- -----------------------------------
12038 VALID 54092 enq: TX - row lock contention
The full table scan on the child table
and those associated locks can all be avoided by having an index on the FK
constraint column, as these indexes can then be used to effectively police the
constraint during such delete operation.
So what does AI does in this scenario?
Currently it does nothing. In the Oracle database 21c(21.3) currently we can
see an index in unusable/invisible
ai_demo@ATP21C> select
index_name,index_type,indexing,auto,
2 status,visibility
3 from all_indexes
4 where table_name in ('DEMO035P','DEMO035C')
5 and owner ='AI_DEMO';
INDEX_NAME INDEX_TYPE INDEXIN AUT
STATUS VISIBILIT
------------------------- ---------- ------- --- -------- ---------
SYS_AI_7zysyhnb663y9 NORMAL FULL YES UNUSABLE INVISIBLE
PK_DEMO035P NORMAL FULL NO VALID VISIBLE
ai_demo@ATP21C> select
table_name,index_name,column_name,column_position
2 from all_ind_columns
3 where table_name in ('DEMO035P','DEMO035C')
4 order by 1,2;
TABLE_NAME INDEX_NAME COLUMN_NAME COLUMN_POSITION
---------- ------------------------- --------------- ---------------
DEMO035C SYS_AI_7zysyhnb663y9 CID 1
DEMO035P PK_DEMO035P ID 1
So you may need to manually create such an index on the FK constraint to improve the performance and eliminate the locking issues.
ai_demo@ATP21C> create
index idx_demo035c on demo035c(cid);
Index created.
ai_demo@ATP21C> set
autotrace traceonly exp statistics
ai_demo@ATP21C> delete from demo035p where id = 56;
1 row deleted.
Execution Plan
----------------------------------------------------------
Plan hash value: 4215583649
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1 | 4 | 1 (0)| 00:00:01 |
| 1 | DELETE | DEMO035P | | | | |
|* 2 | INDEX UNIQUE SCAN| PK_DEMO035P | 1 | 4 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------
Predicate Information
(identified by operation id):
---------------------------------------------------
2 - access("ID"=56)
Note
-----
- automatic DOP: Computed Degree of Parallelism is 1 because of no expensive parallel operation
- PDML disabled because child referential constraints are present
Statistics
----------------------------------------------------------
22 recursive calls
7 db block gets
26 consistent gets
6 physical reads
1276 redo size
340 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
1 rows processed
ai_demo@ATP21C> set
autotrace off
consistent gets were dropped massively as
Oracle can now use the index to avoid the FTS search on the child table. The
associated locking issues are eliminated as well.
2 nologging
3 as
4 select rownum as id , a.*
5 from stage a
6 where rownum <= 10000;
2 nologging
3 as
4 select rownum as pid ,
5 mod(rownum,1000)+1000 as cid ,
6 a.*
7 from stage a, stage b, stage c
8 where rownum <= 10000000 ;
2 add constraint pk_demo035p
3 primary key(id);
2 add constraint fk_demo035c
3 foreign key(cid)
4 references demo035p;
ai_demo@ATP21C> delete from demo035p where id = 55;
----------------------------------------------------------
Plan hash value: 4215583649
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1 | 4 | 1 (0)| 00:00:01 |
| 1 | DELETE | DEMO035P | | | | |
|* 2 | INDEX UNIQUE SCAN| PK_DEMO035P | 1 | 4 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------
---------------------------------------------------
-----
- automatic DOP: Computed Degree of Parallelism is 1 because of no expensive parallel operation
- PDML disabled because child referential constraints are present
----------------------------------------------------------
55 recursive calls
12 db block gets
164019 consistent gets
6 physical reads
2596 redo size
340 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
5 sorts (memory)
0 sorts (disk)
1 rows processed
----- ----------
1 54092
----- ----------
1 12038
2 from v$locked_object l,
3 user_objects u
4 where l.object_id = u.object_id;
---------- ---------- ---------- -----------
136548 DEMO035P 12038 3
136548 DEMO035P 54092 3
136549 DEMO035C 54092 3
2 from gv$session
3 where sid = 12038;
---------- ----------- ---------------- -----------------------------------
12038 VALID 54092 enq: TX - row lock contention
2 status,visibility
3 from all_indexes
4 where table_name in ('DEMO035P','DEMO035C')
5 and owner ='AI_DEMO';
------------------------- ---------- ------- --- -------- ---------
SYS_AI_7zysyhnb663y9 NORMAL FULL YES UNUSABLE INVISIBLE
PK_DEMO035P NORMAL FULL NO VALID VISIBLE
2 from all_ind_columns
3 where table_name in ('DEMO035P','DEMO035C')
4 order by 1,2;
---------- ------------------------- --------------- ---------------
DEMO035C SYS_AI_7zysyhnb663y9 CID 1
So you may need to manually create such an index on the FK constraint to improve the performance and eliminate the locking issues.
ai_demo@ATP21C> delete from demo035p where id = 56;
----------------------------------------------------------
Plan hash value: 4215583649
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 1 | 4 | 1 (0)| 00:00:01 |
| 1 | DELETE | DEMO035P | | | | |
|* 2 | INDEX UNIQUE SCAN| PK_DEMO035P | 1 | 4 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------
---------------------------------------------------
-----
- automatic DOP: Computed Degree of Parallelism is 1 because of no expensive parallel operation
- PDML disabled because child referential constraints are present
----------------------------------------------------------
22 recursive calls
7 db block gets
26 consistent gets
6 physical reads
1276 redo size
340 bytes sent via SQL*Net to client
520 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
3 sorts (memory)
0 sorts (disk)
1 rows processed