Isolation levels determine
the extent to which data being accessed or modified in one transaction
is protected from changes to the data by other transactions. In theory,
each transaction should be fully isolated from other transactions.
However, in practice, for practical and
performance reasons, this might not always be the case. In a concurrent
environment in the absence of locking and isolation, the following four
scenarios can happen:
Lost update—
In this scenario, no isolation is provided to a transaction from other
transactions. Multiple transactions can read the same copy of data and
modify it. The last transaction to modify the data set prevails, and the
changes by all other transactions are lost.
Dirty reads—
In this scenario, one transaction can read data that is being modified
by other transactions. The data read by the first transaction is
inconsistent because the other transaction might choose to roll back the
changes.
Nonrepeatable reads—
In this scenario, which is somewhat similar to zero isolation, a
transaction reads the data twice, but before the second read occurs,
another transaction modifies the data; therefore, the values read by the
first read are different from those of the second read. Because the
reads are not guaranteed to be repeatable each time, this scenario is
called nonrepeatable reads.
Phantom reads—
This scenario is similar to nonrepeatable reads. However, instead of
the actual rows that were read changing before the transaction is
complete, additional rows are added to the table, resulting in a
different set of rows being read the second time. Consider a scenario in
which Transaction A reads rows with key values within the range of 1
through 5 and returns three rows with key values 1, 3, and 5. Before
Transaction A reads the data again within the transaction, Transaction B
adds two more rows with the key values 2 and 4 and commits the changes.
Assuming that Transaction A and Transaction B both can run
independently without blocking each other, when Transaction A runs the
query a second time, it now gets five rows with key values 1, 2, 3, 4,
and 5. This phenomenon is called phantom reads because in the second pass, you get records you did not expect to retrieve.
Ideally, a DBMS must provide levels of isolation to
prevent these types of scenarios. Sometimes, for practical and
performance reasons, databases relax some of the rules. The American
National Standards Institute (ANSI) has defined four transaction
isolation levels, each providing a different degree of isolation to
cover the previous scenarios. ANSI SQL-92 defines the following four
standards for transaction isolation:
SQL Server 2008 supports all the ANSI isolation
levels; in addition, SQL Server 2008 also supports two additional
transaction isolation levels that use row versioning. One is an
alternative implementation of Read Committed isolation called Read
Committed Snapshot, and the other is the Snapshot transaction isolation
level.
You can set the default transaction isolation for a user session by using the SET TRANSACTION ISOLATION LEVEL T-SQL command, or for individual SQL statements, you can specify table-level isolation hints within the query.
Read Uncommitted Isolation
If you set the Read Uncommitted mode for a session, no isolation is provided to the SELECT
queries in that session. A transaction that is running with this
isolation level is not immune to dirty reads, nonrepeatable reads, or
phantom reads.
To set the Read Uncommitted mode for a session, you run the following statements from the client:
T-SQL— Use SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.
ODBC— Use the function call SQLSetConnectAttr with Attribute set to SQL_ATTR_TXN_ISOLATION and ValuePtr set to SQL_TXN_READ_UNCOMMITTED.
OLE DB— Use the function call ITransactionLocal::StartTransaction with the isoLevel set to ISOLATIONLEVEL_READUNCOMMITTED.
ADO— Set the IsolationLevel property of the Connection object to adXactReadUncommitted.
ADO.NET— For applications using the System.Data.SqlClient managed namespace, call the SqlConnection.BeginTransaction method and set the IsolationLevel option to ReadUncommitted.
You need to be careful when running queries at Read
Uncommitted isolation; it is possible to read changes that have been
made to data that are subsequently rolled back. In essence, the accuracy
of the results cannot be guaranteed. You should use this mode only when
you need to get information quickly from an online transaction
processing (OLTP) database, without affecting or being affected by the
ongoing updates and when the accuracy of the results is not critical.
Read Committed Isolation
The Read Committed mode is the default
locking-isolation mode for SQL Server. With Read Committed as the
transaction isolation level, read operations can read pages only for
transactions that have already been committed. No “dirty reads” are
allowed. Locks acquired by update transactions are held for the duration
of the transaction. However, in this mode, read requests within the
transaction release locks as soon as the query finishes reading the
data. Although this improves concurrent access to the data for updates,
it does not prevent nonrepeatable reads or phantom reads. For example,
within a transaction, a process could read one set of rows early in the
transaction and then, before reading the information again, another
process could modify the result set, resulting in a different result set
being read the second time.
Because Read Committed is the default isolation level
for SQL Server, you do not need to do anything to set this mode. If you
need to set the isolation level back to Read Committed mode for a
session, you run the following statements from the client:
T-SQL— Use SET TRANSACTION ISOLATION LEVEL READ COMMITTED.
ODBC— Use the function call SQLSetConnectAttr with Attribute set to SQL_ATTR_TXN_ISOLATION and ValuePtr set to SQL_TXN_READ_COMMITTED.
OLE DB— Use the function call ITransactionLocal::StartTransaction with isoLevel set to ISOLATIONLEVEL_READCOMMITTED.
ADO— Set the IsolationLevel property of the Connection object to adXactReadcommitted.
ADO.NET— For applications using the System.Data.SqlClient managed namespace, call the SqlConnection.BeginTransaction method and set the IsolationLevel option to ReadCommitted.
Read Committed Snapshot Isolation
When the READ_COMMITTED_SNAPSHOT database option is set to ON,
sessions running with the Read Committed isolation mode use row
versioning to provide statement-level read consistency. When this
database option is enabled and a transaction runs at the Read Committed
isolation level, all statements within the transaction see a snapshot of
the data as it exists at the start of the statement.
When the READ_COMMITTED_SNAPSHOT option is
enabled for a database, SQL Server maintains versions of each row that
is modified. Whenever a transaction modifies a row, an image of the row
before modification is copied into a page in the version store, which is
a collection of data pages in tempdb. If multiple transactions
modify a row, multiple versions of the row are linked in a version
chain. Queries running with Read Committed Snapshot isolation retrieve
the last version of each row that had been committed when the statement
started, providing a statement-level snapshot of the data.
In the Read Committed Snapshot isolation mode, read
operations do not acquire shared page or row locks on the data.
Therefore, readers using row versioning do not block other processes
modifying the same data, and, similarly, processes modifying the data do
not block the readers. In addition, because the read operations do not
acquire locks, locking overhead is reduced. However, processes modifying
data still block other processes modifying data because two operations
cannot modify the same data at the same time. Exclusive locks on
modified data are still acquired and held until the end of the
transaction.
While locking overhead is reduced for read operations
when using Read Committed Snapshot isolation, it does introduce
overhead to maintain the row versions in tempdb. In addition, tempdb must have sufficient space to hold the row versions in addition to the space required for normal tempdb operations.
You might want to consider enabling the READ_COMMITTED_SNAPSHOT
database option when blocking that occurs between read and write
operations affects performance to the point that the overhead of
creating and managing row versions is offset by the concurrency
benefits. You may also consider using Read Committed Snapshot isolation
when an application requires absolute accuracy for long-running
aggregations or queries where data values must be consistent to the
point in time that the query starts.
Note
You
can use Read Committed Snapshot isolation mode with most existing SQL
Server applications without making any change to the application code
itself if the applications are written to use the default Read Committed
isolation level. The behavior of Read Committed, whether to use row
versioning or not, is determined by the database option setting, and
this can be enabled or disabled without requiring any changes to the
application code.
Repeatable Read Isolation
In Repeatable Read mode, SQL Server provides the same
level of isolation for updates as in Read Committed mode, but it also
allows the data to be read many times within the same transaction and
guarantees that the same values will be read each time. Repeatable Read
isolation mode prevents other users from updating data that has been
read within the transaction until the transaction in which it was read
is committed or rolled back. This way, the reading transaction does not
pick up changes to the rows it read previously within the transaction.
However, this isolation mode does not prevent additional rows (that is,
phantom reads) from appearing in the subsequent reads.
Although preventing nonrepeatable reads is desirable
for certain transactions, it requires holding locks on the data that has
been read until the transaction is completed. This reduces concurrent
access for multiple update operations and causes performance degradation
due to lock waits and locking contention between transactions. It can
also potentially lead to deadlocks.
To set Repeatable Read mode for a session, you run the following statements from the client:
T-SQL— Use SET TRANSACTION ISOLATION LEVEL REPEATABLE READ.
ODBC— Use the function call SQLSetConnectAttr with Attribute set to SQL_ATTR_TXN_ISOLATION and ValuePtr set to SQL_TXN_REPEATABLEREAD.
OLE DB— Use the function call ITransactionLocal::StartTransaction with isoLevel set to ISOLATIONLEVEL_REPEATABLEREAD.
ADO— Set the IsolationLevel property of the Connection object to adXact REPEATABLEREAD.
ADO.NET— For applications using the System.Data.SqlClient managed namespace, call the SqlConnection.BeginTransaction method and set the IsolationLevel option to RepeatableRead.
Serializable Read Isolation
Serializable Read mode is similar to repeatable reads
but adds to it the restriction that rows cannot be added to a result
set that was read previously within a transaction. This prevents phantom
reads. In other words, Serializable Read locks the existing data being
read as well as rows that do not yet exist. It accomplishes this by
locking the data being read. In addition, SQL Server puts locks on the range of values being read so that additional rows cannot be added to the range.
For example, say you run a query in a transaction that retrieves all records for the Sales table in the bigpubs2008 database for a store with the stor_id of 7066. To prevent additional sales records from being added to the Sales table for this store, SQL Server locks the range of values with stor_id of 7066.
Although preventing phantom reads is desirable for
certain transactions, Serializable Read mode, like Repeatable Read,
reduces concurrent access for multiple update operations and can cause
performance degradation due to lock waits and locking contention between
transactions, and it can potentially lead to deadlocks.
To set Serializable Read mode for a session, you run the following statements from the client:
T-SQL— Use SET TRANSACTION ISOLATION LEVEL SERIALIZABLE.
ODBC— Use the function call SQLSetConnectAttr with Attribute set to SQL_ATTR_TXN_ISOLATION and ValuePtr set to SQL_TXN_SERIALIZABLE.
OLE DB— Use the function call ITransactionLocal::StartTransaction with isoLevel set to ISOLATIONLEVEL_SERIALIZABLE.
ADO— Set the IsolationLevel property of the Connection object to adXact SERIALIZABLE.
ADO.NET— For applications using the System.Data.SqlClient managed namespace, call the SqlConnection.BeginTransaction method and set the IsolationLevel option to Serializable.
Snapshot Isolation
Snapshot Isolation is an additional isolation level
available in SQL Server 2008. Similar to Read Committed Snapshot,
Snapshot Isolation mode uses row versioning to take a point-in-time
snapshot of the data. However, unlike Read Committed Snapshot isolation,
which provides a statement-level snapshot of the data, Snapshot
Isolation maintains a snapshot of the data for the duration of the
entire transaction. A data snapshot is taken when the transaction starts
and the snapshot remains consistent for the duration of the
transaction.
Snapshot Isolation mode provides the benefit of
repeatable reads without acquiring and holding shared locks on the data
that is read. This can help minimize locking and blocking problems
between read operations and update operations. Read operations do not
have to wait for write operations and writes don’t have to wait for
reads.
To set the Snapshot Isolation mode for a session, you run the following statement:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
In addition, to be able to request the Snapshot Isolation mode in a session, you must enable the database option ALLOW_SNAPSHOT_ISOLATION with the ALTER DATABASE command:
ALTER DATABASE dbname SET ALLOW_SNAPSHOT_ISOLATION ON
When Snapshot Isolation mode is enabled, SQL Server
assigns a transaction sequence number to each transaction that
manipulates data using row versioning. When either the READ_COMMITTED_SNAPSHOT or ALLOW_SNAPSHOT_ISOLATION database option is set to ON, SQL Server stores a version of the previously committed image of the data row in tempdb
whenever the row is modified by a transaction. Each of these versions
is marked with the transaction sequence number of the transaction that
made the change. The versions of the modified rows are linked together
in a chain, with the most recent version of the row always stored in the
current database and the versioned rows stored in tempdb.
When a transaction requests a read of data, it
searches the version chain to locate the last committed version of the
data row with a lower transaction sequence number than the current
transaction. Row versions are kept in tempdb only long enough
to satisfy the requirements of any transactions running under row
versioning–based isolation levels. SQL Server keeps track of the
sequence number of the oldest outstanding transaction and periodically
deletes all row versions stamped with transaction sequence numbers lower
than that.
You might consider using snapshot isolation in the following instances:
When you want optimistic concurrency control
When it is unlikely that your transaction would have to be rolled back because of an update conflict
When an application generates reports based on long-running, multistatement queries that must have point-in-time consistency
With systems that are incurring a high number of deadlocks because of read/write contention
There is a risk to using snapshot isolation, however.
If two client applications both retrieve the same data and then both
attempt to write changes to the data back to the database, the second
application could potentially overwrite the first application’s changes.
This is called a lost update error.
Fortunately, SQL Server 2008 resolves this problem by blocking the
second transaction’s writes. So, although snapshot isolation provides
benefits for resolving conflicts between read and write operations,
there can still be conflicts between multiple write operations. For
systems with heavy read and insert activity and with little concurrent
updating of the same resource, snapshot isolation can provide a solution
for concurrency issues.
Another cost of snapshot isolation is that it can make heavy use of tempdb. For this reason, you should locate tempdb on its own high-performance drive system.
Note
Only
one of the transaction isolation levels can be active at any given time
for a user session. The isolation level you set within an application
is active for the duration of the connection or until it is manually
reset. To check the current transaction isolation level settings, you
run the DBCC USEROPTIONS command and examine the value for isolation
level, as in the following example:
DBCC USEROPTIONS
go
Set Option Value
---------------------------------- --------------
textsize 2147483647
language us_english
dateformat mdy
datefirst 7
lock_timeout -1
quoted_identifier SET
arithabort SET
ansi_null_dflt_on SET
ansi_warnings SET
ansi_padding SET
ansi_nulls SET
concat_null_yields_null SET
isolation level snapshot
Be aware that DBCC USEROPTIONS reports an isolation level of Read Committed Snapshot when the database option READ_COMMITTED_SNAPSHOT is set to ON
and the current transaction isolation level is set to Read Committed.
The actual isolation level in effect for the user session is Read
Committed.