|
|
ALLBASE/SQL Reference Manual: HP 9000 Computer Systems > Chapter 5 Concurrency Control
through Locks and Isolation LevelsResolving Conflicts among Concurrent Transactions |
|
Several kinds of conflict can occur between transactions that are contending for access to the same data. The following three are typical cases:
The first conflict results in a lock wait, which simply means that the second transaction must wait until the first transaction releases the lock. The second conflict is known as conventional deadlock, which is automatically resolved by ALLBASE/SQL. The third conflict is an undetectable deadlock, which cannot be automatically resolved. When a transaction is waiting for a lock, the application pauses until the lock can be acquired. When a transaction is in a wait state, some other transaction already has a lock on the row, page, or table that is needed. When the transaction that is holding a lock on the requested row, page, or table releases its lock through a COMMIT WORK or ROLLBACK WORK statement, the waiting transaction can then acquire a new lock and proceed. The amount of time an application waits for a lock depends on the timeout value. A timeout value is the amount of time a user waits if a requested database resource is unavailable. If an application times out while waiting for a lock, an error occurs and the transaction is rolled back. See the SET USER TIMEOUT statement in the "SQL Statements" chapter of this manual for more information. The larger the number of lock waits, the slower the performance of the DBEnvironment as a whole. You can observe the lock waits at any given moment in the DBEnvironment by issuing the following query:
The use of isolation levels less severe than Repeatable Read can improve concurrency by reducing lock waits. For example, reporting applications that do not depend on perfect consistency can use the Read Uncommitted level, while applications that scan an entire table to update just a few rows can use Read Committed with REFETCH or Read Uncommitted with REFETCH for the greatest concurrency. Applications that intend to update a larger number of rows can use Cursor Stability. You can set the amount of time a transaction will wait for a lock by using the SET USER TIMEOUT statement, or by setting a default timeout value using the ALTDBE command in SQLUtil. If no timeout value is set as a default, the transaction will wait until the resource is released. Consult your database administrator about default timeout values. The second kind of conflict is known as a deadlock between two transactions. This happens when two transactions both need data or indexes that the other already has locked. Deadlocks involving system catalog pages are also possible. ALLBASE/SQL detects and resolves deadlocks when they occur. If different priority numbers are assigned to the transactions in the BEGIN WORK statement, the transaction with the larger priority number is rolled back. If no priorities are assigned, the more recent transaction is rolled back. ALLBASE/SQL resolves deadlocks between two transactions at a time. Therefore, if more than two transactions are deadlocked at one time, the transaction aborted may not be the transaction with the largest priority number or the newest transaction among all transactions deadlocked. By default, the action taken to resolve a deadlock is to roll back one of the transactions. However, it is also possible to set the deadlock action for a transaction to roll back the current command instead of the entire transaction by using the SET SESSION or SET TRANSACTION statements. Specific table types are likely to incur particular types of deadlock. Two transactions can deadlock on the same PUBLIC or PUBLICROW table when the transactions attempt to access the same page or row. The larger the table, the less likely it is that two transactions will need to access the same page or row, so deadlock is reduced. If the table is small, there is less chance of deadlock when it is defined PUBLICROW rather than PUBLIC. The following scenario illustrates the development of a deadlock involving two fairly large PUBLIC tables with indexes in the sample DBEnvironment PartsDBE. Assume that both transactions are at the RR isolation level.
This sequence results in a deadlock which causes ALLBASE/SQL to choose a transaction to roll back. In the example, since no priorities are assigned, ALLBASE/SQL rolls back both of user 2's queries and displays an error message. User 1's second update then completes. Title not available shows the deadlock condition that results from the previous example. The use of PRIVATE tables ensures there will be no deadlock on the same table, because access to the table is serialized. However, deadlock across two or more tables is common with PUBLICREAD and PRIVATE tables that are accessed by different transactions in different order. The following example shows a deadlock involving a PRIVATE table:
A common deadlock scenario for PUBLICREAD tables is to do a SELECT, thus obtaining a table level share lock, and then an UPDATE, which must upgrade the lock to exclusive:
The need to upgrade frequently results in deadlock. The size of a table is another factor affecting its susceptibility to deadlock. If the table is small, it is highly probable that several users may need the same pages, so deadlocks may be relatively frequent when page level locking is used. The probability of collision is highest when the table is small and its rows are also small, with many stored on one page. If the table is large, it is relatively unlikely that multiple users will want the same pages at the same time, so page level locking should cause relatively few deadlocks. The tradeoff between deadlock and throughput is one of the central issues in concurrency control. It is important to minimize the number of deadlocks while permitting the greatest possible concurrent access to database tables. To avoid deadlock among multiple tables, be sure to have all transactions access them in the same order. This can often be done by modifying programs to use the same algorithms to access data in the same order (for example, first update table 1, then table 2), rather than accessing data in random order. This strategy cannot always be followed, but when it can be used, processes will wait their turn to use a particular data object rather than deadlocking. You can avoid deadlocks that stem from upgrading locks by designing transactions that use SIX locks, which have the effect of serializing updates on a table while permitting concurrent reads. To employ SIX locks, read the table with a cursor that includes a FOR UPDATE clause. You can also obtain SIX locks by using the LOCK TABLE statement, specifying the SHARE UPDATE option. Locking at the table level should reduce deadlocks when all or most pages in a PUBLIC table (rows in a PUBLICROW table) are accessed in a query. Locking the table in share update mode obtains SIX locks on the table and its pages (or rows) when you are reading data with the intention of updating some data. The use of PUBLICREAD and PRIVATE tables decreases the chance of encountering a deadlock by forcing serialization of updates within a single table, that is, requiring one update transaction to be committed before another can obtain any locks on the same table. Obviously, this reduces concurrency during update operations. You can also use the LOCK TABLE statement for transactions on PUBLICREAD tables that read data prior to updating it. In applications that declare cursors explicitly, you can use the KEEP CURSOR option in the OPEN statement to release exclusive locks as quickly as possible. When you use the KEEP CURSOR option for a cursor you explicitly open in a program, you can use the COMMIT WORK statement to end the transaction and release locks without losing the cursor's position. Furthermore, you can either retain or release the locks on the page or row pointed to by the current cursor position. When you use the KEEP CURSOR option, your transaction holds individual exclusive locks only for a very short time. Thus, the chance of deadlock is reduced, and throughput is improved dramatically. For details, refer to the chapter entitled "Processing with Cursors" in the ALLBASE/SQL application programming guide for the language of your choice. Applications that connect to multiple DBEnvironments may encounter deadlocks that cannot be detected and resolved by ALLBASE/SQL. An example follows:
This kind of deadlock is called undetectable because ALLBASE/SQL can only detect a deadlock within a single DBEnvironment session. It is your responsibility to coordinate your system's use of distributed transactions so as to prevent undetectable deadlock. You can enable ALLBASE/SQL to identify and roll back what probably are undetectable deadlocks by setting appropriate user timeout values for each DBEnvironment connection. For more information refer to "Using Multiple Connections and Transactions with Timeouts" in Chapter 2 “Using ALLBASE/SQL” A similar condition known as an undetectable wait state can also arise when you are using multi-connect functionality. An undetectable wait occurs when you connect more than once to the same DBEnvironment from the same application in multi-transaction mode and attempt to obtain resources held by your other connection. For example:
In this instance, you are waiting on your own resources. To avoid situations like this, be sure to set user timeout values when you use multi-connect functionality. |
|