Snapshot isolation is designed to improve concurrency and consistency by maintaining different versions of data.
Blocks and deadlocks can occur when users are reading and writing the same data simultaneously. When you use snapshot isolation in a transaction, the database server returns a committed version of the data in response to any read requests. It does this without acquiring read locks, and prevents interference with users who are writing data.
A snapshot is a set of data that has been committed in the database. When using snapshot isolation, all queries within a transaction use the same set of data. No locks are acquired on database tables, which allows other transactions to access and modify the data without blocking. Open snapshot transactions require the database server to keep copies of all data modified by other transactions to the database. Minimize the performance impact of snapshot transactions by limiting them to small transactions.
Three snapshot isolation levels that let you control when a snapshot is taken are supported:
Use a snapshot of committed data from the time when the first row is read, inserted, updated, or deleted by the transaction.
Use a snapshot of committed data from the time when the first row is read by the statement. Each statement within the transaction sees a snapshot of data from a different time.
For read-only statements, use a snapshot of committed data from the time when the first row is read. Each read-only statement within the transaction sees a snapshot of data from a different time. For insert, update, and delete statements, use the isolation level specified by the updatable_statement_isolation option (can be one of 0 (the default), 1, 2, or 3).
You also have the option of specifying when the snapshot starts for a transaction by using the BEGIN SNAPSHOT statement.
Snapshot isolation is often useful, such as:
Snapshot transactions acquire write locks only for statements that modify the database. If a transaction is performing mainly read operations, then the snapshot transaction does not acquire read locks that could interfere with other users' transactions.
Snapshot transactions do not acquire read locks, which makes data available to other users for reading and updating while the snapshot transaction takes place.
Because a snapshot shows a committed set of data from a specific point in time, you can use snapshot isolation to see consistent data that does not change throughout the transaction, even if other users are making changes to the data while your transaction is running.
Snapshot isolation only affects base tables and global temporary tables that are shared by all users. A read operation on any other table type never sees an old version of the data, and never initiates a snapshot. The only time where an update to another table type initiates a snapshot is if the isolation_level option is set to snapshot, and the update initiates a transaction.
The following statements cannot be executed when there are cursors opened with the WITH HOLD clause that use either statement or transaction snapshots:
When opening cursors with the WITH HOLD clause, a snapshot of all rows committed at the snapshot start time is visible. Also visible are all modifications completed by the current connection since the start of the transaction within which the cursor was opened.
TRUNCATE TABLE is allowed only when a fast truncation is not performed because in this case, individual DELETEs are then recorded in the transaction log.
In addition, if any of these statements are performed from a non-snapshot transaction, then snapshot transactions that are already in progress that subsequently try to use the table return an error indicating that the schema has changed.
Materialized view matching avoids using a view if it was refreshed after the start of the snapshot for a transaction.
Snapshot isolation levels are supported in all programming interfaces. You can set the isolation level using the SET OPTION statement.
When snapshot isolation is enabled for a database, each time a row is updated, the database server adds a copy of the original row to the version stored in the temporary file. The original row version entries are stored until all the active snapshot transactions complete that might need access to the original row values. A transaction using snapshot isolation sees only committed values, so if the update to a row was not committed or rolled back before a snapshot transaction began, the snapshot transaction needs to access the original row value. This allows transactions using snapshot isolation to view data without placing any locks on the underlying tables.
The VersionStorePages database property returns the number of pages in the temporary file that are currently being used for the version store. To obtain this value, execute the following query:
SELECT DB_PROPERTY ( 'VersionStorePages' );
Old row version entries are removed when they are no longer needed. Old versions of BLOBs are stored in the original table, not the temporary file, until they are no longer required, and index entries for old row versions are stored in the original index until they are not required.
You can retrieve the amount of free space in the temporary file using the sa_disk_free_space system procedure.
If a trigger is fired that updates row values, the original values of those rows are also stored in the temporary file.
Designing your application to use shorter transactions and shorter snapshots reduces temporary file space requirements.
If you are concerned about temporary file growth, you can set up a GrowTemp system event that specifies the actions to take when the temporary file reaches a specific size.