What we’ll be doing Back to Basics Read Consistency Write Consistency
Multiversioning -- Just talk about it for a bit… In my opinion the fundamental difference between Oracle and most of the rest It can be the best feature It can be the worst feature (if you don’t get it) Non blocking reads Writes only block writes However… unless you understand it, you’re probably doing some transactions wrong in your system! (DIY RI is almost always wrong)
What does it do… Block 1 scn:123 Block 2 scn:55 Block 3 scn:100 Query Begins, SCN=200 This block is OK Query Continues, SCN=200 This block is OK Block 3 scn:210 Update happens This block is modified Query Continues, SCN=200 This block is not OK Query Continues, SCN=200 This block is OK Block 3 scn:<=200 Query Continues, SCN=200 This block is OK
Why do you care Suppose you were pulling changes…. T-1: update some data at 11:59, do not commit, time recorded though T0: 12 noon, start initial ‘pull’, remember this time (for refreshing later). T.5: commit the T-1 transaction. It was not visible at T0, it is at T1 T1: Later on, go pull everything updated since 12 noon T1.5: But – we will not see the T-1 update!! T2: After committing T1’s work, you have all of the data right?
Why do you care I am trying to enforce the business rule that if an employee has one or more addresses, then one and at least one address must be "preferred", that is, preferred = 'Y'. All other addresses for the employee must have preferred = 'N'. An address does not have to exist for an employee (but if one does, at least/most one must be preferred) Seems simple enough, but… cr1.sql
Why do you care It is either virtually impossible or so inefficient to enforce integrity: ACROSS objects ACROSS rows in an object By yourself… Without locking tables or large sets of rows
Could it be part of your performance problem… Long running queries against volatile tables Queries against small “hot” table Cr2.sql
Write consistency So, what happens when…. You start updating lots of rows using a full scan You hit a locked row near the end (you block) The blocker commits What happens there??
Write consistency So, what happens when…. You start updating lots of rows using a full scan You hit a locked row near the end (you block) The blocker commits What happens there?? And what happens if some other transaction comes along and re-locks that row? Cr3.sql (remember to go forward a slide to recap observations)
Write consistency Observations We updated row 5,000 and kept it locked Started bulk update – it made 4,999 entries in the audit table and then blocked. Updated ids 1..4,999 We committed the change – this caused the bulk update to rollback. While it was busy rolling back, we locked row 10,000 in the other session The bulk update blocked much faster this time – it was in select for update mode now. Did not really do the updates, just getting locks It got blocked again (but had rows 1..9,999 all to itself) We committed the blocking session Bulk update finally went back and updated all of the rows. End up with 4,999 + 2 + 10,000 trigger fires
Write consistency Real question (from the groups and asktom) Setup: create table accounts ( acc int primary key, amount int, pending int, cleared date ); Initially the table contains the following: ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 10 -2 2 0 2 3 0 0 26-NOV-03
Write consistency Implied Business Rules (that no one is enforcing) Sum(pending) where cleared is null = 0 Pending = 0 when cleared is NOT NULL Initially the table contains the following: ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 10 -2 2 0 2 3 0 0 26-NOV-03
Write consistency So, there is a committed database state with a pending funds transfer of 2 dollars from acc 1 to acc 2. Let's submit another transfer of 1 dollar from acc 1 to acc 3 but do not commit it yet in SQL*Plus Session 1: update accounts set pending = pending - 1, cleared = null where acc = 1; set pending = pending + 1, cleared = null where acc = 3; ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 10 -3 2 0 2 3 0 1 Initially the table contains the following: ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 10 -2 2 0 2 3 0 0 26-NOV-03
Write consistency And now let's clear all the pending transfers in SQL*Plus Session 2 in a single-statement read-committed transaction: update accounts set amount = amount + pending, pending = 0, cleared = sysdate where cleared is null; Session 2 naturally blocks immediately on row 1 But – Session 2’s SCN for reading was fixed at this point in time, the time the update started. It “reads” the table as of the time the update began Session 2 sees this view of the data – two rows where cleared is null Initially the table contains the following: ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 10 -2 2 0 2 3 0 0 26-NOV-03
Write consistency update accounts set amount = amount + pending, pending = 0, cleared = sysdate where cleared is null; Now commit the transaction in session 1. Session 2 readily unblocks and the answer is: ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 7 0 26-NOV-03 2 2 0 26-NOV-03 3 0 1 Session 1 did this: ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 10 -3 2 0 2 3 0 1 Initially the table contains the following: ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 10 -2 2 0 2 3 0 0 26-NOV-03
Write consistency Here we go - the results produced by a single-statement transaction read committed transaction in session 2, are inconsistent -- the second funds transfer has not completed in full. Session 2 should have produced the following instead: ACC AMOUNT PENDING CLEARED ---------- ---------- ---------- --------- 1 7 0 26-NOV-03 2 2 0 26-NOV-03 3 1 0 26-NOV-03 And not this: 3 0 1
Write consistency What we’ll discover is the set of rows to be updated (as defined by the predicate) is defined 99.99% by the predicate. The rows to be modified were identified “as of the update submission time” in this case (only the rows that had CLEARED is NULL were updated) That the observer observing can muck things up became readily apparent! (triggers are taken into consideration) Cr4.sql it works as he expected it, why…. Cr5.sql it works as he observed, why….. If a trigger references :old.pending (but not :new)…. Cr6.sql toggle :new/:old http://asktom.oracle.com/~tkyte/wc.html
Questions Q&A and Answers