As developers, we don’t always think too deeply about the database internals and that can result in poor performance and unexpected concurrency issues.

This article represents my learning progress in diving into SQL Server’s concurrency features.

In this article we’ll discuss transactions and isolation levels / read modes.

By default, when you write SQL it runs in an autocommit transaction, which means each statement will be committed to the database after it is executed.

This is fine for many cases, but what if you have a complex operation where you want to modify multiple tables in one batch and these changes must all go together or not at all in order to prevent data integrity issues. While constraints can help enforce data integrity, sometimes you need something more.

Transactions are here to help.

Per SQL Server’s Documentation

A transaction is a single unit of work. If a transaction is successful, all of the data modifications made during the transaction are committed and become a permanent part of the database. If a transaction encounters errors and must be canceled or rolled back, then all of the data modifications are erased.

A transaction looks like the following:

Here we want to insert into the Resumes table and also update the ResumeCounts table in one transaction. If the second statement errored, we’d want to rollback the transaction and remove Matt Eland from the Resumes table. This transaction takes care of that for us.

Additionally, you can use a ROLLBACK TRANSACTION statement if you just want to see if a statement executes and get a sense of how many rows are updated. I often recommend running statements in a ROLLBACK TRANSACTION to check for errors and as a sanity check before changing the transaction to a COMMIT TRANSACTION.

Transactions have something called a read mode that governs what types of locking strategies they use.

Read Committed

By default, SQL Server will run in this mode and only read data that is committed (not dirty — currently being modified by another query). This prevents what is called dirty reads.

To activate this mode, execute the following statement:

SET TRANSACTION ISOLATION LEVEL READ COMMITTED

Read Uncommitted

Read Uncommitted removes the restriction that data must be in a fully committed state. This means that read uncommitted bypasses some locks in order to read rows which may currently have intermediate values.

To activate this mode, execute the following statement:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

Repeatable Read

Repeatable Read adds an additional layer of safety to Read Uncommitted. In addition to reading things that have not yet been committed, Repeatable Read acquires a shared lock on the rows in question to prevent them from being further updated until the transaction ends. This means that any subsequent reads of those rows during the transaction will produce the same result. This is not necessarily true with Read Uncommitted.

Generally the extra overhead of the shared locks makes Repeatable Read less desirable, but there may be cases where it is good to use them.

To activate this mode, execute the following statement:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ

Serializable

Serializable is an extreme measure to demand total control of the range of rows being modified. It will not read uncommitted and it will not allow other transactions to read data that it is modifying. As such, it requires exclusive locks, causing other transactions to wait for it to complete. This also prevents other rows from being added to the set of records, preventing what is known as a phantom read.

This has high data integrity and safety implications at the expense of performance.

To activate this mode, execute the following statement:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

Snapshot

Snapshot is a SQL Server specific option that effectively takes a snapshot of the rows being evaluated at the start of the transaction.

This bypasses other locking operations on the database, but any updates that occur during the transaction will not be available to the transaction. Additionally, the database must have snapshots enabled via the ALLOW_SNAPSHOT_ISOLATION option.

To activate this mode, execute the following statement:

SET TRANSACTION ISOLATION LEVEL SNAPSHOT

Transactions are a good tool for managing data integrity during update and insert scripts which much touch multiple tables.

Next in this series we’ll discuss locking.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.