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 9 ■ REDO AND UNDO 333 • A long-running query begins. This query will ultimately read some of those blocks from earlier. This query starts at SCN t1, the read consistent SCN it must roll back data to in order to achieve read consistency. The transaction entry for the modification transaction is still in the undo segment transaction table when we began. • During the query, many commits are made in the system. These transactions do not touch the blocks in question (if they did, then we wouldn’t have the impending problem). • The transaction tables in the undo segments roll around and reuse slots due to the high degree of COMMITs. Most important, the transaction entry for the original modification transaction is cycled over and reused. In addition, the system has reused undo segment extents, so as to prevent a consistent read on the undo segment header block itself. • Additionally, the lowest SCN recorded in the undo segment now exceeds t1 (it is higher than the read-consistent SCN of the query), due to the large number of commits. When our query gets to the block that was modified and committed before it began, it is in trouble. Normally, it would go to the undo segment pointed to by the block and find the status of the transaction that modified it (in other words, it would find the COMMIT SCN of that transaction). If the COMMIT SCN is less than t1, our query can use this block. If the COMMIT SCN is greater than t1, our query must roll back that block. The problem is, however, that our query is unable to determine in this particular case if the COMMIT SCN of the block is greater than or less than t1. It is unsure as to whether it can use that block image or not. The ORA-01555 error then results. To see this, we will create many blocks in a table that need to be cleaned out. We will then open a cursor on that table and allow many small transactions to take place against some other table—not the table we just updated and opened the cursor on. Finally, we will attempt to fetch the data for the cursor. Now, we know that the data required by the cursor will be “OK”—we should be able to see all of it since the modifications to the table would have taken place and been committed before we open the cursor. When we get an ORA-01555 error this time, it will be because of the previously described problem. To set up for this example, we’ll use • The 2MB UNDO_SMALL undo tablespace (again). • A 4MB buffer cache, which is enough to hold about 500 blocks. This is so we can get some dirty blocks flushed to disk to observe this phenomenon. Before we start, we’ll create the big table we’ll be querying: ops$tkyte@ORA10G> create table big 2 as 3 select a.*, rpad('*',1000,'*') data 4 from all_objects a; Table created. ops$tkyte@ORA10G> exec dbms_stats.gather_table_stats( user, 'BIG' ); PL/SQL procedure successfully completed.

334 CHAPTER 9 ■ REDO AND UNDO That table will have lots of blocks as we get about six or seven rows per block using that big data field. Next, we’ll create the small table that the many little transactions will modify: ops$tkyte@ORA10G> create table small ( x int, y char(500) ); Table created. ops$tkyte@ORA10G> insert into small select rownum, 'x' from all_users; 38 rows created. ops$tkyte@ORA10G> commit; Commit complete. ops$tkyte@ORA10G> exec dbms_stats.gather_table_stats( user, 'SMALL' ); PL/SQL procedure successfully completed. Now, we’ll dirty up that big table. We have a very small undo tablespace, so we’ll want to update as many blocks of this big table as possible, all while generating the least amount of undo possible. We’ll be using a fancy UPDATE statement to do that. Basically, the following subquery is finding the “first” rowid of a row on every block. That subquery will return a rowid for each and every database block identifying a single row on it. We’ll update that row, setting a VARCHAR2(1) field. This will let us update all of the blocks in the table (some 8,000 plus in the example), flooding the buffer cache with dirty blocks that will have to be written out (we have room for only 500 right now). We’ll make sure we are using that small undo tablespace as well. To accomplish this and not exceed the capacity of our undo tablespace, we’ll craft an UPDATE statement that will update just the “first row” on each block. The ROW_NUMBER() built-in analytic function is instrumental in this operation; it assigns the number 1 to the “first row” by database block in the table, which would be the single row on the block we would update: ops$tkyte@ORA10G> alter system set undo_tablespace = undo_small; System altered. ops$tkyte@ORA10G> update big 2 set temporary = temporary 3 where rowid in 4 ( 5 select r 6 from ( 7 select rowid r, row_number() over (partition by dbms_rowid.rowid_block_number(rowid) order by rowid) rn 8 from big 9 ) 10 where rn = 1 11 ) 12 / 8045 rows updated. ops$tkyte@ORA10G> commit; Commit complete.

CHAPTER 9 ■ REDO AND UNDO 333<br />

• A long-running query begins. This query will ultimately read some of those blocks from<br />

earlier. This query starts at SCN t1, the read consistent SCN it must roll back data to in<br />

order to achieve read consistency. The transaction entry for the modification transaction<br />

is still in the undo segment transaction table when we began.<br />

• During the query, many commits are made in the system. These transactions do not<br />

touch the blocks in question (if they did, then we wouldn’t have the impending<br />

problem).<br />

• The transaction tables in the undo segments roll around <strong>and</strong> reuse slots due to the high<br />

degree of COMMITs. Most important, the transaction entry for the original modification<br />

transaction is cycled over <strong>and</strong> reused. In addition, the system has reused undo segment<br />

extents, so as to prevent a consistent read on the undo segment header block itself.<br />

• Additionally, the lowest SCN recorded in the undo segment now exceeds t1 (it is higher<br />

than the read-consistent SCN of the query), due to the large number of commits.<br />

When our query gets to the block that was modified <strong>and</strong> committed before it began, it is<br />

in trouble. Normally, it would go to the undo segment pointed to by the block <strong>and</strong> find the status<br />

of the transaction that modified it (in other words, it would find the COMMIT SCN of that<br />

transaction). If the COMMIT SCN is less than t1, our query can use this block. If the COMMIT SCN<br />

is greater than t1, our query must roll back that block. The problem is, however, that our<br />

query is unable to determine in this particular case if the COMMIT SCN of the block is greater<br />

than or less than t1. It is unsure as to whether it can use that block image or not. The<br />

ORA-01555 error then results.<br />

To see this, we will create many blocks in a table that need to be cleaned out. We will then<br />

open a cursor on that table <strong>and</strong> allow many small transactions to take place against some<br />

other table—not the table we just updated <strong>and</strong> opened the cursor on. Finally, we will attempt<br />

to fetch the data for the cursor. Now, we know that the data required by the cursor will be<br />

“OK”—we should be able to see all of it since the modifications to the table would have taken<br />

place <strong>and</strong> been committed before we open the cursor. When we get an ORA-01555 error this<br />

time, it will be because of the previously described problem. To set up for this example,<br />

we’ll use<br />

• The 2MB UNDO_SMALL undo tablespace (again).<br />

• A 4MB buffer cache, which is enough to hold about 500 blocks. This is so we can get<br />

some dirty blocks flushed to disk to observe this phenomenon.<br />

Before we start, we’ll create the big table we’ll be querying:<br />

ops$tkyte@ORA10G> create table big<br />

2 as<br />

3 select a.*, rpad('*',1000,'*') data<br />

4 from all_objects a;<br />

Table created.<br />

ops$tkyte@ORA10G> exec dbms_stats.gather_table_stats( user, 'BIG' );<br />

PL/SQL procedure successfully completed.

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

Saved successfully!

Ooh no, something went wrong!