Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005

rekharaghuram
from rekharaghuram More from this publisher
05.11.2015 Views

CHAPTER 6 ■ LOCKING AND LATCHING 205 ops$tkyte@ORA10G> insert into p values ( 2 ); 1 row created. ops$tkyte@ORA10G> commit; Commit complete. and then we execute the following: ops$tkyte@ORA10G> insert into c values ( 2 ); 1 row created. nothing untoward happens yet. But if we go into another session and attempt to delete the first parent record ops$tkyte@ORA10G> delete from p where x = 1; we’ll find that session gets immediately blocked. It is attempting to gain a full table lock on table C before it does the delete. Now no other session can initiate a DELETE, INSERT, or UPDATE of any rows in C (the sessions that had already started may continue, but no new sessions may start to modify C). This blocking would happen with an update of the primary key value as well. Because updating a primary key is a huge no-no in a relational database, this is generally not an issue with updates. Where I have seen this updating of the primary key become a serious issue is when developers use tools that generate SQL for them, and those tools update every single column, regardless of whether the end user actually modified that column or not. For example, say that we use Oracle Forms and create a default layout on any table. Oracle Forms by default will generate an update that modifies every single column in the table we choose to display. If we build a default layout on the DEPT table and include all three fields, Oracle Forms will execute the following command whenever we modify any of the columns of the DEPT table: update dept set deptno=:1,dname=:2,loc=:3 where rowid=:4 In this case, if the EMP table has a foreign key to DEPT and there is no index on the DEPTNO column in the EMP table, then the entire EMP table will be locked during an update to DEPT. This is something to watch out for carefully if you are using any tools that generate SQL for you. Even though the value of the primary key does not change, the child table EMP will be locked after the execution of the preceding SQL statement. In the case of Oracle Forms, the solution is to set that table’s UPDATE CHANGED COLUMNS ONLY property to YES. Oracle Forms will generate an UPDATE statement that includes only the changed columns (not the primary key). Problems arising from deletion of a row in a parent table are far more common. As I demonstrated, if I delete a row in table P, then the child table, C, will become locked during the DML operation, thus preventing other updates against C from taking place for the duration of the transaction (assuming no one else was modifying C, of course; in which case the delete will wait). This is where the blocking and deadlock issues come in. By locking the entire table C, I have seriously decreased the concurrency in my database to the point where no one will be able to modify anything in C. In addition, I have increased the probability of a deadlock, since I now “own” lots of data until I commit. The probability that some other session will become blocked on C is now much higher; any session that tries to modify C will get blocked. Therefore, I’ll start seeing lots of sessions that hold some preexisting locks on other resources getting

206 CHAPTER 6 ■ LOCKING AND LATCHING blocked in the database. If any of these blocked sessions are, in fact, locking a resource that my session also needs, we will have a deadlock. The deadlock in this case is caused by my session preventing access to many more resources (in this case, all of the rows in a single table) than it ever needed. When someone complains of deadlocks in the database, I have them run a script that finds unindexed foreign keys, and 99 percent of the time we locate an offending table. By simply indexing that foreign key, the deadlocks—and lots of other contention issues—go away. The following example demonstrates the use of this script to locate the unindexed foreign key in table C: ops$tkyte@ORA10G> column columns format a30 word_wrapped ops$tkyte@ORA10G> column tablename format a15 word_wrapped ops$tkyte@ORA10G> column constraint_name format a15 word_wrapped ops$tkyte@ORA10G> select table_name, constraint_name, 2 cname1 || nvl2(cname2,','||cname2,null) || 3 nvl2(cname3,','||cname3,null) || nvl2(cname4,','||cname4,null) || 4 nvl2(cname5,','||cname5,null) || nvl2(cname6,','||cname6,null) || 5 nvl2(cname7,','||cname7,null) || nvl2(cname8,','||cname8,null) 6 columns 7 from ( select b.table_name, 8 b.constraint_name, 9 max(decode( position, 1, column_name, null )) cname1, 10 max(decode( position, 2, column_name, null )) cname2, 11 max(decode( position, 3, column_name, null )) cname3, 12 max(decode( position, 4, column_name, null )) cname4, 13 max(decode( position, 5, column_name, null )) cname5, 14 max(decode( position, 6, column_name, null )) cname6, 15 max(decode( position, 7, column_name, null )) cname7, 16 max(decode( position, 8, column_name, null )) cname8, 17 count(*) col_cnt 18 from (select substr(table_name,1,30) table_name, 19 substr(constraint_name,1,30) constraint_name, 20 substr(column_name,1,30) column_name, 21 position 22 from user_cons_columns ) a, 23 user_constraints b 24 where a.constraint_name = b.constraint_name 25 and b.constraint_type = 'R' 26 group by b.table_name, b.constraint_name 27 ) cons 28 where col_cnt > ALL 29 ( select count(*) 30 from user_ind_columns i 31 where i.table_name = cons.table_name 32 and i.column_name in (cname1, cname2, cname3, cname4, 33 cname5, cname6, cname7, cname8 )

CHAPTER 6 ■ LOCKING AND LATCHING 205<br />

ops$tkyte@ORA10G> insert into p values ( 2 );<br />

1 row created.<br />

ops$tkyte@ORA10G> commit;<br />

Commit complete.<br />

<strong>and</strong> then we execute the following:<br />

ops$tkyte@ORA10G> insert into c values ( 2 );<br />

1 row created.<br />

nothing untoward happens yet. But if we go into another session <strong>and</strong> attempt to delete the<br />

first parent record<br />

ops$tkyte@ORA10G> delete from p where x = 1;<br />

we’ll find that session gets immediately blocked. It is attempting to gain a full table lock on<br />

table C before it does the delete. Now no other session can initiate a DELETE, INSERT, or UPDATE<br />

of any rows in C (the sessions that had already started may continue, but no new sessions may<br />

start to modify C).<br />

This blocking would happen with an update of the primary key value as well. Because<br />

updating a primary key is a huge no-no in a relational database, this is generally not an issue<br />

with updates. Where I have seen this updating of the primary key become a serious issue is<br />

when developers use tools that generate SQL for them, <strong>and</strong> those tools update every single<br />

column, regardless of whether the end user actually modified that column or not. For example,<br />

say that we use <strong>Oracle</strong> Forms <strong>and</strong> create a default layout on any table. <strong>Oracle</strong> Forms by<br />

default will generate an update that modifies every single column in the table we choose to<br />

display. If we build a default layout on the DEPT table <strong>and</strong> include all three fields, <strong>Oracle</strong> Forms<br />

will execute the following comm<strong>and</strong> whenever we modify any of the columns of the DEPT<br />

table:<br />

update dept set deptno=:1,dname=:2,loc=:3 where rowid=:4<br />

In this case, if the EMP table has a foreign key to DEPT <strong>and</strong> there is no index on the DEPTNO<br />

column in the EMP table, then the entire EMP table will be locked during an update to DEPT. This<br />

is something to watch out for carefully if you are using any tools that generate SQL for you.<br />

Even though the value of the primary key does not change, the child table EMP will be locked<br />

after the execution of the preceding SQL statement. In the case of <strong>Oracle</strong> Forms, the solution<br />

is to set that table’s UPDATE CHANGED COLUMNS ONLY property to YES. <strong>Oracle</strong> Forms will generate<br />

an UPDATE statement that includes only the changed columns (not the primary key).<br />

Problems arising from deletion of a row in a parent table are far more common. As I<br />

demonstrated, if I delete a row in table P, then the child table, C, will become locked during the<br />

DML operation, thus preventing other updates against C from taking place for the duration of<br />

the transaction (assuming no one else was modifying C, of course; in which case the delete will<br />

wait). This is where the blocking <strong>and</strong> deadlock issues come in. By locking the entire table C,<br />

I have seriously decreased the concurrency in my database to the point where no one will be<br />

able to modify anything in C. In addition, I have increased the probability of a deadlock, since<br />

I now “own” lots of data until I commit. The probability that some other session will become<br />

blocked on C is now much higher; any session that tries to modify C will get blocked. Therefore,<br />

I’ll start seeing lots of sessions that hold some preexisting locks on other resources getting

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!