TABLOCKX not working - sql-server

I am playing with my SQL Server 2012 trying to get a hold of the locks. Based on a tutorial I saw, I tried to test obtaining an exclusive lock on a table so that no other query would be able to read information from it, until the transaction is not over, but its just not working. Even though it was working in the video, here is my query in the first window :
use TSQL2012
BEGIN transaction
update tele with (TABLOCKX, holdlock)
set cor = '12'
waitfor delay '00:05'
go
then in a second query window I simply tried:
select * from tele
and it worked fine, although on theory there should have been "exclusive" lock preventing that. Why is that happening? I tried also with
set transaction isolation level serializable on
and also without the delay but the select is always successful. Any ideas?

I've tried this on two different tables and can easily reproduce what you've found. Here is what is happening:
If Sql-Server is returning rows to the SELECT query before the table is updated then its VERY probable that at that moment, there are already locks on your tele table that are incompatible with an exclusive lock. For example, if there is another session somewhere that already has a reader lock on the table then your UPDATE statement will be SUSPENDED with a WAIT_TYPE of LCK_M_X, meaning that your UPDATE is blocked, waiting for locks that are incompatible with an exclusive lock to be released. Since all locks are incompatible with an exclusive table lock then when any other session is accessing the table with any kind of lock, your update statement will be suspended.
In a 3rd instance of SQL Management Studio, right click your server and open activity monitor. Filter for your database and login and re-run the experiment. If the table is something that applications use frequently and the SELECT runs before the UPDATE, then you will notice a LCK_M_X WAIT_TYPE on the UPDATE.
Try creating a new table that only you know about and re-run the experiment. It should work.

Related

Can I conditionally avoid hitting a trigger completely?

We had to use a trigger to sync an old system to the new system until we can fully deprecate the old system. The new system doesn't need this trigger at all and, in fact, exits out immediately on the condition that it's the new app.
The impact on the old system is acceptable.
However, the impact on the new system is not because the new system processes many, many more records on a single update. Merely executing the trigger changes an update from 10 seconds (already "UGH") to over a minute and a half.
The new system performs acceptably by disabling the trigger in code (VS Core with EntityFramework btw), running the update and then re-enabling the code, all within a transaction. There is disagreement among my colleagues about whether or not the trigger is disabled for the other application while the transaction is being processed.
I have already seen this post:
https://dba.stackexchange.com/questions/204339/sql-server-how-to-disable-trigger-for-an-update-only-for-your-current-session
And the first answer is the solution I am using. My colleagues tell me that won't work. I believe it will. But the answers 2-whatever seem to contradict the first answer.
My testing proved out the first answer as well but I need to be 100% sure on this.
TIA
However, the impact on the new system is not because the new system processes many, many more records on a single update.
You should find a way to batch the updates into fewer statements. The trigger fires per statement, not per row. EG EF Core does batching automatically, or you can use a TVP or SqlBulkCopy into a temp table, etc.
DISABLE TRIGGER within a transaction eliminates the possibility of other users updating while the trigger is disabled
Yes. You can easily verify that disabling the trigger takes a Sch-M lock on the table for the duration of the transaction, which is incompatible with all other table access.
eg
use tempdb
drop table if exists t
create table t(id int primary key)
go
create trigger t_t on t after insert
as
begin
select 'trigger running' msg
end
go
begin transaction
go
disable trigger t_t on t
go
select object_name(resource_associated_entity_id) table_name, resource_lock_partition, request_mode, request_status
from sys.dm_tran_locks
where request_session_id = ##spid
and resource_type = 'OBJECT'
order by 1,2
rollback

SQL Server 2008 - how to raise error, when trying to update locked row?

In my application, several users have the ability to read or modify the same tables. However, when one row is modified by a user, the others should not be able to use it anymore.
I am using Transactions with ISOLATION LEVEL READ UNCOMMITED. The Problem is, that when a user updates a row in a table and another user tries to update the same row - the second Transaction will wait for the first Transaction to ROLLBACK/COMMIT, before it can update the row.
What i want to do is to raise an error in this Situation, for the second user - so he does not have to wait so long.
How can I do this??
The Problem is that the Transactions are quite large (several procedures included), so there would be a posibility of blocking some users for quite some time because of the lock.
Thank you
You might be able to use a NOWAIT hint when the second user attempts his query from his own transaction:
User one
BEGIN TRAN
SELECT *
FROM someTable
WHERE blah
User two
(first user's transaction still open)
BEGIN TRAN
SELECT *
FROM someTable WITH(NOWAIT)
WHERE blah
I recall reading somewhere that only Oracle and Postgres support fail fast lock acquisitions. If so, then the above hint might be ignored.

Best way to track locks - SQL Server

We have a new sp getting released and during testing we found that when it runs its blocking other OLTP transactions. We found that initially it was because the new sp was causing lock escalation on a table and we reduced the number of batch size and was able to avoid that. even after avoiding lock escalation, it is still blocking oltp transactions that are coming in.
I think its locking the same row which the oltp transaction is updating.
I need to find a way to track all the locks held and release by the new sp. I tried trace/xevents(lock acquired/release) and it does not look like its capturing all the lock, may be cause it happens so fast.
Just to understand how lock acquired look like, i tested it out by doing a select * from atable. but it gives me different results. When we do select * doesnt it put a series of page locks, so i should be seeing shared page locks in the trace. but all i see is IS lock acquired and released.
what is the best way to track all the locks for a given transaction?
I ran below query in one session
begin tran
update orderstst
set unitprice=unitprice+1
waitfor delay '00:00:20'
and ran below dmv while the query is running on other session..
select resource_database_id,request_mode,request_type,request_status,txt.text
from sys.dm_tran_locks lck
join
sys.dm_exec_requests ec
on ec.session_id=lck.request_session_id
cross apply
sys.dm_exec_Sql_text(ec.sql_handle) txt
I got below data...
when the transaction is still not committed,but completed,i ran above dmv again.but didnt get any output.since this is not currently executing.
But running below dmv,will still give me lock info of all sessions holding locks..so you will be able to identify which session is holding more locks
select resource_database_id,request_mode,request_type,request_status
from sys.dm_tran_locks lck
join
sys.dm_exec_sessions ec
on ec.session_id=lck.request_session_id
Above query gives me below info..
So in summary,you have to run DMV1 or DMV2 for some period through sql agent job and insert into some table for later analaysis..
Further from SQL 2012,you can use extended events also..
Go to Management ->Extended Events ,Right Click and say ,start new session wizard.
Give it a name and check start at server startup
next screen gives you an option to select default template or not,i choose default template for locks as shown below and click next..
In the next screen,you can choose different events,in channel,select all channels and do the same in categories too and select the events of your interest,i choose below ..
In this screen,you can select actions ,i choose text ,sessionid
In next screen,filter like say for example ..gather events only for a databasename like 'somename' or query like some text..
Next screen is where you can save file to disk for later analysis..
Complete rest of screens and finally select start event session immediately option..
When you are done with gathering data,go to extended events and stop the session you created.Right click and say view target data..which shows you below screenn
EDIT: as of 12/3/2019 the start new session wizard is now located here:

There is lock on the table which persists the disconnection

I have a very small table in my database with only one row in it. When I do select statement on it, my query always times out. I have tried following things:
Conecting/disconnecting from the database server
doing select * from table(nolock)- which works fine and also proves that there is some lock on the table
However, How can I remove the lock from the table and how can I know what is holding the table captive? Also, I am not sure how does this lock survive the disconnection from the database server?

How to efficiently use LOCK_ESCALATION in SQL Server 2008

I'm currently having troubles with frequent deadlocks with a specific user table in SQL Server 2008. Here are some facts about this particular table:
Has a large amount of rows (1 to 2 million)
All the indexes used on this table only have the "use row lock" ticked in their options
Edit: There is only one index on the table which is its primary Key
rows are frequently updated by multiple transactions but are unique (e.g. probably a thousand or more update statements are executed to different unique rows every hour)
the table does not use partitions.
Upon checking the table on sys.tables, I found that the lock_escalation is set to TABLE
I'm very tempted to turn the lock_escalation for this table to DISABLE but I'm not really sure what side effect this would incur. From What I understand, using DISABLE will minimize escalating locks from TABLE level which if combined with the row lock settings of the indexes should theoretically minimize the deadlocks I am encountering..
From what I have read in Determining threshold for lock escalation it seems that locking automatically escalates when a single transaction fetches 5000 rows..
What does a single transaction mean in this sense? A single session/connection getting 5000 rows thru individual update/select statements?
Or is it a single sql update/select statement that fetches 5000 or more rows?
Any insight is appreciated, btw, n00b DBA here
Thanks
LOCK Escalation triggers when a statement holds more than 5000 locks on a SINGLE object. A statement holding 3000 locks each on two different indexes of the same table will not trigger escalation.
When a lock escalation is attempted and a conflicting lock exists on the object, the attempt is aborted and retried after another 1250 locks (held, not acquired)
So if your updates are performed on individual rows and you have a supporting index on the column, then lock escalation is not your issue.
You will be able to verify this using the Locks-> lock escalation event from profiler.
I suggest you capture the deadlock trace to identify the actual cause of the deadlock.
I found this article after a quick Google of disabling table lock escalation. Although not a real answer for the OP I think it is still relevant for one off scripts and note worthy here. There's a nice little trick you can do to temporarily disable table lock escalation.
Open another connection and issue something like.
BEGIN TRAN
SELECT * FROM mytable (UPDLOCK, HOLDLOCK) WHERE 1=0
WAITFOR DELAY '1:00:00'
COMMIT TRAN
as
Lock escalation cannot occur if a different SPID is currently holding
an incompatible table lock.
from microsoft kb

Resources