System.Transactions : ReadUncommitted
I got a question today about setting the isolationlevel on a transaction to ReadUncommitted. The question was why the transaction still locked the row it was updating, when isolationlevel is set to ReadUncommitted. It was expected that using ReadUncommitted on the transaction allowed other requests (on a different connection) to read the data, even when it wasn’t committed. When we take a look at the SQL Server 2005 Books Online you can read the following:
Specifies that statements can read rows that have been modified by other transactions but not yet committed.
When you read this real fast, you might think that statements can read rows that have been modified by transactions, but not yet committed. But it doesn’t say that. It says, “rows that have been modified by other transactions…” So what’s the difference? Let’s look at it through code.
I’ve setup a simple table with two columns. The table is called [MyTable] with a primary key called [MyTableId] of type integer. Also a column called [value] of type varchar(50). I’ve added five rows which you can see on the left.
I’ve got some examples in which I’ll create a transaction, update the 5th row with a new value. In every example I’ve added wait statements so that another connection can try to read the changed value of this 5th row. It’ll either fail because of a lock, or not if done correctly.
The following T-SQL code will setup the transaction’s isolation level to Read Uncommitted (line 1), start a new transaction (line 4) and try to update the table (lines 6-8). It than waits for 15 seconds (line 10) and finally rollback the transaction (line 12). Within these 15 seconds you’ll have time to execute another query in another query-window. For example “select * from MyTable where MyTableId = 5“. You’ll notice it’ll lock-up for 15 seconds.
Why is this happening when IsolationLevel is set to Read Uncommitted? The answer lies in the single word “other”, as stated above. When setting IsolationLevel to Read Uncommitted, you can read rows from another transaction. But you’ll have to set the isolation level on this connection. On the connection you’re reading with! For this you don’t need a new transaction, but you do need to copy line 1 from the T-SQL code above.
We can achieve the same result(s) in .NET. I’m written my examples using .NET 2.0 but I’m (pretty) sure the first example can be used in .NET 1.x as well.
We’ve got two connections set up. On line 17, we set the IsolationLevel to ReadUncommitted. Again this doesn’t make sense for the result we want to achieve at line 34. We execute query1 on line 28, which will create a transactional lock on line 34.
At line 31 however you can see in a single line that we set the IsolationLevel to Read Uncommitted, on the second connection! So if you uncomment line 31, you’ll actually read the data that line 28 is trying to update. Of course it’s not yet committed, but that’s what Read Uncommitted does. In this example we’re not using query3, but it would produce the same result as setting the isolation level. The difference is with query3 we’re not setting Read Uncommitted on the entire connection. Instead we’re only using it with a single query.
So we finally get to using System.Transactions. In the example below we’re using the exact same queries as above. You can see we wrap the TransactionScope around both connections. But we first set up the IsolationLevel in line 2, then pass this to the TransactionScope constructor on line 4. Now both connections automatically enlist in our transaction and also both have the IsolationLevel set to Read Uncommitted.
The problem here is however that we’ll automatically get bumped up to a distributed transaction. So make sure the MSDTC service is running.
Now you know how you can read uncommitted data that’s being updated in other transactions. Be sure to know however that this is called a “dirty read”. You’re reading data that in 99% of the time is data you don’t want. It’s not being updated for nothing! The person who asked me the question actually had a valid reason to choose this, although the initial design of the application might not be the best. But it wasn’t his, so he wasn’t to blame.
Sahil Malik once made a weblog post about why the heck (old weblog) we would want to use ReadUncommitted. He couldn’t think of a single reason to use it. I now know one reason, but I still agree with him. I’m sorry if I wasted your time with this long weblog post, with a final conclusion that you shouldn’t use ReadUncommitted. 😉
Table of contents