An Introduction to
Transaction Management,
Concurrent Access, Lockwaits, and Deadlocks
For Developers
Most databases are transaction processing databases if they
are not data warehouses or data marts.
Transaction management is probably the most important concept that a
developer needs to know if they are going to work with a database.
Definition of Transaction
Management
It is the responsibility of the business analysts and developers to define the transactions and make sure that they are implemented properly. There are two definitions of transaction management depending on your point of view:
· Developer definition: A transactions is a set of database changes (inserts, updates, and deletes) such that you want all of them saved or none of them saved.
· DBA definition: All the DML statements (inserts, updates, and deletes) between two COMMITs, or a COMMIT and a ROLLBACK.
The typical example of a transaction is a program that deposits a check in a bank account. This actually involves two steps:
1. The first account is debited for the amount on the check.
2. The second account is credited for the amount on the check.
You want both of the changes saved or neither of them saved. If the first step fails, for “insufficient funds” or any other reason, then you don’t want to credit the second account. If the second step fails, for “account closed” or any other reason, then you don’t want to debit the first account. Both steps make up a single transactions.
Without proper transaction management, the first step could be saved even if the second step fails, or vice versa. The result is called “inconsistent data.” With proper transaction management, the data is in a consistent state.
Another example of a transaction is the purchase of a widget from an online website. This transaction is made up of at least two steps: debiting your credit card, and sending you the widget. If the charge to your credit card bounces, then they won’t want to send you the widget. If they can’t send you the widget, because they are out of stock or something, then they shouldn’t charge your credit card. Both steps must succeed or the transaction is cancelled.
Implementation of
Transaction Management in PL/SQL
All programming languages can implement transaction management. This is typically done using COMMIT and ROLLBACK statements. You may also see the ANSI compliant COMMIT WORK and ROLLBACK WORK versions of these statements. The following is an outline of a PL/SQL block that implements a transaction:
COMMIT; -- this is the previous
commit
BEGIN
INSERT …
UPDATE …
DELETE …
COMMIT;
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
END;
If no error occurs during the execution of the SQL statements, then the COMMIT statement will run and execution will continue after the END of the block. If any of the SQL statements throws and exception (causes an error) then execution will jump to the exception handling section and the ROLLBACK statement will run. A rollback statement will undo all the changes made since the previous commit. Good PL/SQL coding will contain a lot more than this, but this example illustrates how exception handling is done. All coding languages that connect to databases can issue COMMIT and ROLLBACK statements, and handle exceptions.
This is the most common way to implement transactions, but not the only one.
A transaction can have any number of DML statements in it. It can have thousands, or it can have 1. The statements in the transaction will be defined by the business needs of the application.
Manually
Creating Transactions in SQLPlus.
Here are some examples that create transactions in SQLPlus manually. First let’s create a table and insert a row into it:
SQL1>CREATE
TABLE trans_test(
2
col1 INTEGER,
3
col2 VARCHAR2(2000),
4
constraint trans_test_pk PRIMARY KEY ( col1 )
5 );
Table created.
SQL1>INSERT
INTO trans_test ( col1, col2
) VALUES ( 1, 'Initial value for row1' );
1 row created.
If we query the table, we can see the row, even though it hasn’t been commited yet.
SQL1>SELECT
* FROM trans_test;
COL1 COL2
---------
------------------------------------------------------------
1 Initial value for row1
If we commit, then we still see the row.
SQL1>COMMIT;
Commit
complete.
SQL1>SELECT
* FROM trans_test;
COL1 COL2
---------- -----------------------------------------------------------
1 Initial value for row1
Now, let’s do a rollback test. We insert a second row.
SQL1>INSERT
INTO trans_test ( col1, col2
) VALUES ( 2, 'We are going to roll this back.' );
1 row created.
SQL1>SELECT
* FROM trans_test;
COL1 COL2
----------
-----------------------------------------------------------
1 Initial value for row1
2 We are going to roll this back.
Notice that we can see the row once again. Now, let’s do a rollback and see what happens.
SQL1>ROLLBACK;
Rollback complete.
SQL1>SELECT
* FROM trans_test;
COL1 COL2
----------
-----------------------------------------------------------
1 Initial value for row1
After we issue the ROLLBACK statement, the second row is gone.
Logons
and Logoffs.
When your session connects to a database, your first transaction starts. It will continue until your first commit or rollback. When you disconnect your session normally from the database, most applications will issue an automatic commit. “Commit on exit” is typically default behavior, but most application are configurable, so this behavior can be disabled. If your session is disconnected abnormally from the database, no commit is issued and all your work since the last commit will be lost. This will happen if you disconnect your network cable from your PC, or remove your laptop from the docking station.
Database Crash
If the database crashes, because the power failed, or some other reason, then all uncommitted transactions will be rolled back when the database starts up. This will put the data in a “consistent state.”
Other Sessions
SQL statements in other sessions, with separate database connections, have separate transactions even if they are from the same application.
SQLPlus Exit Options
SQLPlus has commands that can configure exit behavior. For example:
WHENEVER SQLERROR EXIT 3 ROLLBACK
This command will issue a rollback statement and exit if any SQL statement in the script causes an error. In unix, it will also return an exit code of 3 to the shell script or session that ran the SQLPlus script.
Autocommit
Some applications can be configured to autcommit. This means that the application will commit automatically after every SQL statement you issue. You do not need to execute a COMMIT statement yourself. In SQLPlus, this behavior is disabled by default, but can be enabled with the following SQLPlus command:
set autocommit on
and disabled with
set autocommit off
JDBC, on the other hand, has autocommit enabled by default. In order to do transaction management, you must disable autocommit and control the transactions programmatically with your own COMMIT and ROLLBACK statements.
SQL Statements in
Transactions
There are three types of SQL statements:
· DML, Data manipulation language
o INSERT
o UPDATE
o DELETE
o SELECT
· DDL, Data Definition Language
o CREATE (table, index, view or anything else)
o ALTER (table, index, view,or anything else)
o DROP …
· DCL, Data Control Language
o GRANT
o REVOKE
Transactions are made up of DML statements, with the exception of SELECT, which does not make any changes to the database. All DDL and DCL statements have implicit commits before and after them. They will commit your work and they cannot be rolled back. Remember that when coding your transactions.
Calling Procedures
and Firing Triggers
A transaction exists within a database session, also thought of as a connection to the database. All the SQL statements in that session between commits and rollbacks are part of a transaction. If you call a procedure or function, it is part of your transaction. Any COMMIT or ROLLBACK statements in those program units will end your current transaction and start a new one. If you fire a trigger, it is part of your transaction. If you then execute a ROLLBACK, any changes made by the trigger will be undone.
Some database procedures have commits in them, such as DBMS_STATS.GATHER_TABLE_STATS(). You should test your procedure calls to determine if they are going to affect your transactions.
There is an exception for procedures that are created with definer rights. These procedures will contain the line “PRAGMA AUTONOMOUS_TRANSACTION“ in the declaration section. The changes made in such procedures are independent, and any COMMIT or ROLLBACK statements in such procedures will have no effect on the on the calling transaction.
Transactions are typically implemented with database procedures or SQL scripts. They are less common in functions. Functions that are used in SELECT statements cannot have transactions in them. You can’t put them in triggers either since a trigger is part of the transaction of the SQL statement that fires it.
Concurrent Access in
Oracle: update by one session and read by another
Concurrent access is related to transaction management in Oracle. Remember that each session has its own transactions. Let’s see how those transactions interact. We will start with an update by one session and a read by another. First I will insert into the table with one session, and then try to see the new row with a second session.
|
SQL>set sqlprompt SQL1> SQL1>INSERT
INTO trans_test ( col1,
col2 ) VALUES ( 2, 'Can you see me?' ); 1 row
created. SQL1>SELECT
* FROM trans_test; COL1 COL2 ----------
---------------------------------------- 1 Initial value for row1 2 Can you see me? |
|
Notice that I have not commited the insert yet. Now I’m going to log into the database with a second SQLPlus session and select from the table.
|
|
SQL*Plus:
Release 10.2.0.1.0 - Production on Wed Jul 27 22:18:22 2011 Copyright (c)
1982, 2005, Oracle. All rights
reserved. SQL> set sqlprompt SQL2> SQL2> SQL2>column
col2 format A60 SQL2>SELECT
* FROM trans_test; COL1 COL2 ----------
------------------------------------------ 1 Initial value for row1 |
The second session
cannot see the new row yet. Now, I will
go back to the first session and commit the change.
|
SQL1>COMMIT; Commit
complete. |
SQL2>SELECT
* FROM trans_test; COL1 COL2 ----------
--------------------------------- 1 Initial value for row1 2 Can you see me? |
Now the second session
can see the new row.
So the moral of the
story is that in Oracle, no other session can see your DML changes until you
commit your work. Other sessions always
see the last commited version of the data. Their view of the data is always transaction
consistent. There are no dirty reads in
Oracle like there are in certain other RDBMS systems.
If you edit data in a
database using a query tool like Toad, SQL Developer ,
Golden, or SQLPlus, just remember that no other
session can see your changes until you click the commit button.
Concurrent Access in
Oracle: lock waits, update of the same row by two different sessions.
First, we update row 1 of the table from the first session again. Then we will update the same row from session 2.
|
SQL1>UPDATE
trans_test 2
SET col2 = 'Update from session 1' 3
WHERE col1 = 1; 1 row
updated. SQL1>COMMIT; Commit
complete. |
SQL2>UPDATE
trans_test 2
SET col2 = 'Update from session 2' 3
WHERE col1 = 1; 1 row
updated. SQL2>commit; Commit
complete. |
Notice
how that works. We commited
after each update. Now we will
update from session 1 again, but this time we will not commit. When we try to
update from session 2 again, nothing happens.
|
SQL1>UPDATE
trans_test 2
SET col2 = 'Update from session 1' 3
WHERE col1 = 1; 1 row
updated. |
SQL2>UPDATE
trans_test 2
SET col2 = 'Update from session 2' 3
WHERE col1 = 1; |
Session 2 hangs. No error message is given. Session 1 has a lock on the row so that no
other session can edit the same row until session 1 signals that it is done
with it. This happens when session 1
issues a commit. Only then will session
2 stop hanging and complete its update.
|
SQL1>COMMIT; Commit
complete. |
1 row
updated. SQL2> |
Now session 2 has row 1
locked. Session 1 will have to wait if
it tries to update row 1, but notice that it can still update row 2.
|
SQL1>UPDATE
trans_test 2
SET col2 = 'Yes, I can see you' 3
WHERE col1 = 2; 1 row
updated. SQL1>COMMIT; Commit
complete. |
|
Oracle DML locks are
row level locks. There is also a DDL lock on the table, but all
it does is prevent any ALTER TABLE statements from changing the table
definition while an uncommitted transaction still has row locks. That is important to remember if you are
running DDL scripts.
Viewing Locks in
Oracle 11
If you have a session that hangs, the easiest way to find out if
you are waiting for a DML lock held by another user, is to use the V$session view. Lets create another lock wait.
|
SQL1>UPDATE
trans_test 2
SET col2 = 'Update from session 1' 3
WHERE col1 = 1; |
|
Session 1 hangs because session 2 has not commited
its update of row 1 yet. Now we query
the v$session view to see the lock.
|
|
SQL2>column
username format A15 SQL2>column
osuser format A15 SQL2>SELECT
sid, username, osuser, blocking_session 2
FROM v$session 3
WHERE blocking_session is not null; SID USERNAME OSUSER BLOCKING_SESSION ----------
--------------- --------------- ---------------- 197 DW_BAT tysonke 167 |
The blocking_session column indicates a lock wait. You can then query the v$session
view again to find out more information about who is blocking you, such as
application name, module, sql_id, machine, and other
identifying information. An abbreviated
list of columns from the V$session view is used for
the purpose of this example so that the output can fit on the page.
|
|
SQL2>SELECT
sid, username, osuser,
program 2
FROM v$session 3
WHERE sid =
167; SID USERNAME OSUSER PROGRAM ----------
--------------- --------------- ------------------ 167 DW_BAT tysonke sqlplusw.exe |
As you can see, it is
my other session that has the lock. We
can release the lock by entering a commit.
|
SQL2>COMMIT; Commit
complete. |
|
Lock waits can also
occur with insert and delete statements as well as updates.
Deadlocks
A dead lock occurs
when two sessions are waiting for each other.
To see how this happens, we will update row 1 with session 1 without
committing the change. Then we will
update row 2 from session 2 without a commit as well.
|
SQL1>UPDATE trans_test 2 SET col2 = 'Update from session 1' 3 WHERE col1 = 1; 1 row updated. |
SQL2>UPDATE trans_test 2 SET col2 = 'Update from session 2' 3 WHERE col1 = 2; 1 row updated. |
Then we will attempt
to update row 2 from session 1, but we will get a lock wait. When we update row 1 from session 2 we also
get a lock wait.
|
SQL1>UPDATE trans_test 2 SET col2 = 'Update from session 1' 3 WHERE col1 = 2; |
SQL2>UPDATE trans_test 2 SET col2 = 'Update from session 2' 3 WHERE col1 = 1; |
Now both sessions are
waiting for each other. This is called a
deadlock. Oracle automatically detects
deadlocks within three seconds and arbitrarily errors out one of the waiting
SQL statements.
|
UPDATE trans_test * ERROR at line
1: ORA-00060:
deadlock detected while waiting for resource |
|
The second update in
session 1 has returned an error, but the first update still has row 1
locked. Session 2 is still waiting to
update row 1. If we issue a commit in
session 1, the update for session 2 will complete now.
|
SQL1>commit; Commit
complete |
SQL2> 1 row
updated. |
Deadlock errors will usually
send an alert notification to the DBAs, depending on how they have configured
their alerts. Your application should
notify you of the deadlock error just as it should notify you about any error,
but the DBAs may forward the deadlock error message to you as well. The DBA message may include information about
both sessions involved in the deadlock and what SQL they were running.
Deadlocks are
application problems, not database problems.
The developers need to design their applications so that deadlocks do
not occur, or so that the risk of deadlocks is minimized.
Causes of lock waits and deadlocks
When there are major lock
wait or deadlock problems in Oracle, it is frequently because of application design
issues. The application sessions may
have no commit statements at all; they may rely on the “commit on exit”
application behavior. Without commits,
many rows in different tables can be locked until the application session
exits.
Another source of lock
waits and deadlocks is the “SELECT FOR UPDATE” SQL statement. This type of select locks all rows in the
query. If there are no filter predicates
in the query, then all rows in the table will be locked. These rows will remain locked until a commit
or rollback is issued. If this SQL statement
is issued by a query tool by someone who then goes out for lunch, other users
may wonder why their sessions are hanging.
Data Warehouses and Datamarts
Data Warehouses and Datamarts are typically not considered transaction
processing databases. However, from the
database point of view, all DML changes to them are still made using
transactions. These transactions may not
be defined by business needs, but they will still be composed of all the DML
statements between commits. All the
considerations involving concurrent access, lock
waits, and deadlocks still apply.
When loading data into
data warehouses, the main consideration is not transaction management, but a
similar form of error handling. The
question that must be addressed is “what happens if any step in the data load causes
an error?” Can you simple restart the
data load? If so, will it pick up from
where it left off, or start over from the beginning? Will data cleanup be needed before the
restart? (This is best avoided.)
Your data load may be
one big transaction, where the entire load is saved if it completes
successfully, or none if it is saved if there is an error. On the other hand, your data load may be
divided up into many small transactions.
It may even commit one row at a time.
Such implementation decisions are typically governed by the need for
effective error handling, and may also be governed by performance needs.
The point is that data
loads are ultimately made by DML statements between commits. The database will consider these to be
transactions even if you don’t.
There may still be a
need for consistent data at the end of the data load, but it may be different
from the simple transaction management model.
Additional information
This introduction only
covers some of the subjects related to transaction management. Other subjects include:
·
Transaction
savepoints to be used with ROLLBACK TO SAVEPOINT.
·
Repeatable
reads with read-only transactions.
·
Serializable transactions
·
Transaction
management of distributed queries over database links(two
phase commit.)
·
Much
more
This presentation has
been tailored for developers. For DBAs,
there are other aspects of transaction management which includes rollback
segments, redo logs, system change numbers (SCN), consistent database recovery,
and other subjects.
For more information,
see:
·
Oracle
11 Concepts manual
o
Chapter
10, Transactions
o
Chapter
9, Data Concurrency and Consistency
·
Oracle
11 Advanced Application Developer’s Guide
o
Chapter
1, SQL Processing for Application Developers
·
Oracle
11 SQL Reference Manual entries for
o
COMMIT
o
ROLLBACK
o
SAVEPOINT
o
SET
TRANSACTION
o
LOCK
TABLE
·
You
can also Google “Database Transaction Management” where you will find the
Wikipedia entry among other sites.