SQL Server: serializable level not working - sql

I have the following SP:
CREATE PROCEDURE [dbo].[sp_LockReader]
AS
BEGIN
SET NOCOUNT ON;
begin try
set transaction isolation level serializable
begin tran
select * from teste
commit tran
end try
begin catch
rollback tran
set transaction isolation level READ COMMITTED
end catch
set transaction isolation level READ COMMITTED
END
The table "test" has many values, so "select * from teste" takes several seconds. I run the sp_LockReader at same time in two diferent query windows and the second one starts showing test table contents without the first one terminates.
Shouldn't serializeble level forces the second query to wait?
How do i get the described behaviour?
Thanks

SERIALIZABLE at the most basic means "hold locks for longer". When you SELECT, the held lock is a shared lock which allows other readers.
If you want to block readers, use WITH (TABLOCKX) hint to take an exclusive lock where you don't need SERIALIZABLE. Or XLOCK with SERIALIZABLE
In other words:
SERIALIZABLE = Isolation Level = lock duration, concurrency
XLOCK = mode= sharing/exclusivity
TABLOCK = Granularity = what is locked
TABLOCKX = combined
See this question/answer for more info

A serializable transaction whose output is not affected by other concurrent transactions. In your case, you are SELECTing twice from the table; neither of those transactions changes the result set of the other, so they may both run simultaneously.
Even if one transaction did update the table, this would not necessarily prevent the other from executing, as the database may work from snapshots.
Have a look here for a better explanation than I can provide... http://en.wikipedia.org/wiki/Isolation_%28database_systems%29

Another note here. If you're using XLOCK under a SERIALIZABLE isolation, other transactions with READ COMMITTED isolation will still be able to read XLOCK'ed rows.
To prevent that, use PAGLOCK along with XLOCK.
See here for details
http://support.microsoft.com/kb/324417

Related

Critical section in query SQL Server stored procedure

I have a stored procedure and I want to run it once at the same time.
In the other hand Like lock variable in C# programing.
Can anyone help me?
for this situation you can use 2 solution for your critical section and the block you want to only one time a time
First use SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
For example
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
begin Tran
your Query
Commit Tran
Second Use DeadLock priority normal
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
begin Tran
your Query
Commit Tran

Serializable isolation level - does it block read?

I have read that serializable isolation level blocks only: insert, update, delete but NOT read.
I have run in one window:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE [dbo].[Categories]
SET name = 'aaa'
WHERE categoryid = 4;
-- without commit
And in the second window:
SELECT TOP (1000) [CategoryId]
,[Name]
FROM [dbo].[Categories]
And the above query is waiting for the end of the first query. So does serialization isolation level block also read?
Yes it blocks reads. Your transaction is not committed, so you can still do a rollback. That means the data you have updated should not be read by any other process before you commit.
"does serialization isolation level block also read" : No.
Serializable acquires some lock (in Ms Sql its shared lock + range lock) which prevents writes from other transactions.
Its the UPDATE statement's exclusive lock (the above shared lock now converts to exclusive lock) that prevents second transaction's SELECT (Read Commited or Repeatable Read by deafult depending on db) to wait.

Isolation levels and select for update

What is the relation between isolation levels of relational databases and select for update?
If I use the plain vanilla JDBC connectivity with SQL Server and set isolation level to READ_REPEATABLE and use a simple select, would I see inconsistency in repeatable reads or not? Or should I use select for update always to avoid inconsistent repeatable reads in transactions? If so, what is the deal with isolation levels, how they would come into play?
SQL Server doesn't have the select ... for update syntax. The equivalent in SQL Server is to use the UPDLOCK table hint.
This hint is used when reading a row and immediately updating the same row in an atomic transaction. EG
declare #balance = (select balance from account where accountId = #id)
update account set balance = #balance + #amount where accountId = #id
At READ COMMITTED, or in any isolation level without a mult-statement transaction, multiple sessions can run the first query, and have lost updates when updating the balance.
Using the REPEATABLE READ or SERIALIZABLE isolation level will prevent this update anomaly, but they do it by blocking the first writer if there are any concurrent transactions that have read the row, and if one of the other transactions attempts to update the row, causing a deadlock.
Mostly this behavior is not worth the performance cost and annoyance of handling deadlocks. So you use 'select for update' aka UPDLOCK to place a U lock on the row while reading and block subsequent readers from acquiring a conflicting lock.
eg
declare #balance = (select balance from account with (updlock) where accountId = #id)
update account set balance = #balance + #amount where accountId = #id
Not exactly sure what you mean by "inconsistent repeatable reads" but with the REPEATABLE READ isolation level in SQL Server, shared locks will be held until the end of the transaction. Only committed data will be returned and other sessions cannot modify the rows read until the transaction is committed (or auto-committed in the case of no explict transaction).
REPEATABLE READ does not prevent other sessions from inserting new rows so rerunning the same query in the same transaction may return new rows (phantom reads) that were not returned originally.
The documentation describes this in more detail along with the other transaction isolation levels.
There is no need to intervene with the default behavior using hints or otherwise.
SQL Server guarantees transaction isolation levels per your declared required level.
REPEATABLE READ guarantees that within a transaction, your statements will see a consistent view of the data that was read, but doesn't guarantee there will be no phantom rows. To avoid the latter you will need to use SERIALIZABLE.
The higher the isolation, the higher the concurrency penalty of course.
See the JDBC Page for isolation levels and the general page for SET TRANSACTION ISOLATION LEVEL for a somewhat more in-depth discussion.
HTH

How to implement Serializable Isolation Level in SQL Server

I need to implement a serializable isolation level in SQL Server but I've tried many ways and I don't get it.
I need to lock 1 row in one transaction (It doesn´t matter if lock the complete table). So, another transaction can´t even select the row (don´t read).
The last thing I tried:
For transaction 1:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
SELECT code FROM table1 WHERE code = 1
-- Here I select in another instance the same row
COMMIT TRAN
For transaction 2:
BEGIN TRAN
SELECT code FROM table1 WHERE code = 1
COMMIT TRAN
I would expect that transaction 2 wait until transaction 1 commit the operation, but the transaction 2 gives me the row.
Anyone can explain me if I miss something?
SQL Server conforms to the strict definition of a Serializable query. That is, there must be a result that can logically be generated IF both queries ran in serial order - Transaction 1 finishing before Transaction 2 can start, or vice versa.
This results in some effects that can be different than you would expect. There is a great explanation of the Serializable isolation level over at SQLPerformance.com that makes clear some of what this logical serializability ends up meaning. (Very helpful site, that one.)
For your above queries, there is no logical requirement to prevent the second query from reading the same row as the first query. No matter in what order the queries are run, they will both return the same data without modifying it. Since the Query Analyzer can identify this, there is no reason to place a read lock on the data. However, if one of the queries performed an update on the data, then (warning - logic assumption here, since I don't actually know the internals of how SQL Server handles this) the QA would set a stronger lock on the selected rows.
TL;DR - SQL Server wants to minimize blocking, so it uses logical analysis to see what types of locks are needed for a serializable isolation level, and it (tries to) use the minimum number and strength of locks needed to achieve its goal.
Now that we've dealt with that - there are only two ways that I can think of to lock a row so that no one else can read it: using XLOCK + TABLOCK (locking the whole table - not a recommended practice) or having some form of a field on each row that is updated when you start your process - something like an SPID field, or a bit flag for Locked. When you update it within your transaction, only SELECTs with NOLOCK hints will be able to read it.
Clearly, neither of these are optimal. I recommend the "This row is busy - go away" flag, as that's probably the approach I would take for an (almost) absolute lock on a row.
According to the documentation:
SERIALIZABLE Specifies the following:
Statements cannot read data that has been modified but not yet committed by other transactions.
No other transactions can modify data that has been read by the current transaction until the current transaction completes.
Other transactions cannot insert new rows with key values that would fall in the range of keys read by any statements in the
current transaction until the current transaction completes.
If you're not making any changes to data with an INSERT, UPDATE, or DELETE inside transaction 1, SQL will release the Shared Lock after the read completes.
What you might want to try is adding a table hit to prevent the row lock from being released until the end of transaction 1.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN
SELECT code
FROM table1 WITH(ROWLOCK, HOLDLOCK)
WHERE code = 1
COMMIT TRAN
Maybe you can solve this with some hack like this?
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
UPDATE someTableForThisHack set val = CASE WHEN val = 1 THEN 0 else 1 End
SELECT code from table1.....
COMMIT TRANSACTION
So you create a table someTableForThisHack and insert one row to it.

Update inside a read uncommitted transaction

I am having an SP with transaction isolation level set as Read Uncommitted.
For Example
Create Procedure TrailSP
AS
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN TRY
UPDATE TrialTable
SET TrailColumn ='Update'
WHERE TrailID=1
--this is followed by more updates and selects
END TRY
BEGIN CATCH
RETURN -1;
END CATCH
RETURN 0;
what I want to know is that the first update I have given in the SP will it get committed instantly as it executes or will it get committed along with the rest of the logic at the end of Sp.
It will get committed, as any update under any transaction isolation level, when the transaction commits. This has nothing to do with the stored procedure ending.
If the call to your procedure has a transaction, then the commit will occur when that transaction commits.
If the call to your procedure does not have a transaction but the session has enabled implicit transactions then it will commit when the application explicitly commits.
If the call to your procedure does not have a transaction and session has the auto commit transaction behavior (ie. the most common case) then transaction will commit when the UPDATE statement completes.
Enabling READ UNCOMMITTED for an UPDATE is a no-op.
-Any data read inside a READ UNCOMMITTED is data that could disappear because the transaction that wrote it rollback.
-Its also possible to not see some row that have been committed because a transaction that have not yet committed and might never commit deleted it.
-Its also possible for row to be missing or be duplicated because of PageSplit.
Basically anything is possible, so the data read should never be used to compute anything that should be written to the database or you risk corrupting your database.
TLDR: never use READ UNCOMMITTED