After reading this interesting article I have some questions.
This table shows a deadlock situation :
T1 holds X lock on all rows with c1=5 on table t_lock1 while T2 holds
X lock on all rows with C1=1 on table t_lock2.
Now each of these transactions wants to update the rows previously
locked by the other. This results in a deadlock.
Question #1
Do transactions obtain locks? I know that reading from table is done by a shared lock, and write to a table is done using an exclusive lock (I'm talking about the default locking settings).
So it seems from this example that transaction also holds a lock ....is it correct?
Question #2
...T1 holds X lock on all rows with c1=5 on table t_lock1...
IMHO as I've said the locking is not per row (although it can be made, but the author didn't mentioned it) - so why does he say : on all rows with C1=5 ?
Do transactions obtain locks?
No. The statement that you execute - a SELECT or an UPDATE will acquire the locks. Depending on your transaction isolation level setting, the duration of how long the (shared) locks (for a reading SELECT) will be held differs - that's all. Shared locks normally are held only very briefly, while update and exclusive locks are held until the transaction ends. The transaction might hold the locks - but it's not the transaction that acquires the locks...
*...T1 holds X lock on all rows with c1=5 on table t_lock1...*
IMHO as I've said the locking is not per row ( although it can be made , but the author didn't mentioned it) so why does he say : on all rows with C1=5 ?
The locking is per row - by default. But why do you think there's only a single row with C1=5? There could be multiple - possibly thousands - and the UPDATE statement will lock all those rows affected by the UPDATE statement.
For question 1: SQL Server reads the source tables rows using U-locks, then updates them converting them to X-locks only on those rows which qualify for the update. Notice the distinction between reading many rows, then filtering them down to those which get written. Those two sets are locked differently.
As there are no selects in your queries only U and X locks are taken. S-lock are not taken for update-queries on the table being updated. This is a heuristic deadlock-avoidance scheme.
Question 2: Locking can be done at different granularity but for low row counts it is usually per row (and this can be forced). Maybe the author assumes an index on C1 which would mean that only the rows with C1=1 need to be read and locked. All other rows wouldn't be touched.
If there was no index SQL Server would indeed read all rows of the table, U-lock them while doing that and then X-lock those which satisfy C1=1. The author indeed mentiones that only rows with C1=1 are x-locked.
Related
In a database, we would not like the table to be dropped during we are modifying a row in this table. Per my understanding, a read lock on table + a write lock on row when write a row in table should be enough(based on that a write lock is needed when drop the table), why do we need a intent lock in this case? seems many databases using intent lock which confused me very much. I think pthread_rwlock should be enough.
I read here that they only exists for performance. Imagine you want to drop a table but you would have to check for every row if its locked or not - that would be time consuming, and you would have to lock every row that you checked.
Heres a citation from the blog post:
From a technical perspective the Intent Locks are not really needed by
SQL Server. They have to do with performance optimization. Let’s have
a look on that in more detail. With an Intent Lock SQL Server just
indicates at a higher level within the Lock Hierarchy that you have
acquired a Lock somewhere else. A Intent Shared Lock tells SQL Server
that there is a Shared Lock somewhere else. A Intent Update or Intent
Exclusive Lock does the same, but this time SQL Server knows that
there is an Update Lock or an Exclusive Lock somewhere. It is just an
indication, nothing more.
But how does that indication help SQL Server with performance
optimization? Imagine you want to acquire an Exclusive Lock at the
table level. In that case, SQL Server has to know if there is an
incompatible lock (like a Shared or Update Lock) somewhere else on a
record. Without Intent Locks SQL Server would have to check every
record to see if an incompatible lock has been granted.
But with an Intent Shared Lock on the table level, SQL Server knows
immediately that a Shared Lock has been granted somewhere else, and
therefore an Exclusive Lock can’t be granted at the table level.
That’s the whole reason why Intent Locks exist in SQL Server: to allow
efficient checking if an incompatible lock exists somewhere within the
Lock Hierarchy. Quite easy, isn’t it?
read lock on table + a write lock on row
This would break meaning of the read lock on the table.
Assume concurrent SELECT operation, which expects unmodified table during execution. This operation will take read lock on the table ... and it will succeed in your implementation. This is bad, as table is actually modified during row modification.
Instead, follow locks combination is used for modify row in the table:
IX(Intent eXclusive) on table + X(eXclusive, similar to "write lock") on row
This combination is compatible (that is, can be executed concurrently) with modification of another row, but it is incompatible with
S(Share, similar to "read lock") on table
used by SELECT.
Locks compatibility table can be found, e.g., on wiki.
One of the conclusions today is "intent lock can lock a parent node AND all its children nodes in a read only mode in a cheaper/safer way".
Take an example for making a table read only case, how to lock it in S-X mode?
We lock the table in S mode, then user still can modify the rows with S(table) + W(row) way. to avoid that, we need to lock every row in a S mode to make sure rows will not be updated. The cost is so huge, and it has a bug that user can insert new rows as well. -- cost too much and not safe.
We lock the table in X mode, How other can read the rows (S on table + S on row), no way, since mode_X on table blocked MODE_S on table. That's not read only.
The right solution with the intent lock is:
Lock the table in MODE_S. that's all!
any intention to modify the rows needs to take a MODE_IX lock on the table, but it is blocked by MODE_S. the solution is cheap/efficient and safe!
We are trying to determine more efficient ways to perform some database operations.
One of the issues that we have is with an ancient primary key system where the primary key for a new record is selected by finding the MAX value in the table and then adding 1 (we cannot change this implementation, please don't suggest this as an answer).
There are some different approaches that we can take to resolve this issue (table-valued parameters, temp tables, etc), but we can never assume that another process won't insert another record during this process and business rules will not allow us to lock the table.
So, the crux of my question is, if we get the current MAX value in a sub-query using an UPDLOCK hint, will the lock hint last for the life of the containing query?
For example:
INSERT
INTO table1
( PKColumn,
DataColumn1,
DataColumn2 )
VALUES SELECT MAX(ISNULL(PKColumn, 0) + 1) FROM table1 WITH (UPDLOCK)) + RowNumber ,
DataColumn1 ,
DataColumn2
FROM #Table1Temp
If we use this to insert 100,000 records, for example, will the UPDLOCK hint hold on the table until all records are inserted or is it released as soon as the initial value is retrieved?
Specifies that update locks are to be taken and held until the
transaction completes. UPDLOCK takes update locks for read operations
only at the row-level or page-level. If UPDLOCK is combined with
TABLOCK, or a table-level lock is taken for some other reason, an
exclusive (X) lock will be taken instead.
So yes. The transaction will last at least as long as that statement. (Possibly longer if you aren't using auto commit transactions and have multiple statements in a transaction)
I am issuing the following query with an UPDLOCK applied:
select #local_var = Column
from table (UPDLOCK)
where OtherColumn = #parameter
What happens is that multiple connections hit this routine which is used inside a stored procedure to compute a unique id. Once the lock acquires we compute the next id, update the value in the row and commit. This is done because the client has a specific formatting requirement for certain Object ID's in their system.
The UPDLOCK locks the correct row and blocks the other processes, but every now and then we get a duplicate id. It seems the local variable is given the current value before the row is locked. I had assumed that the lock would be obtained before the select portion of the statement was processed.
I am using SQLServer 2012 and the isolation level is set to read committed.
If there is other information required, just let me know. Or if I am doing something obviously stupid, that information is also welcome.
From the SQL Server documentation on UPDLOCK:
Use update locks instead of shared locks while reading a table, and hold locks until the end of the statement or transaction. UPDLOCK has the advantage of allowing you to read data (without blocking other readers) and update it later with the assurance that the data has not changed since you last read it.
That means that other processes can still read the values.
Try using XLOCK instead, that will lock other reads out as well.
I think the issue is that your lock is only being held during this Select.
So once your Stored Proc has the Value, it releases the Lock, BEFORE it goes on to update the id (or insert a new row or whatever).
This means that another query running in Parallel is able to Query for the same value and then Update/Insert the same row.
You should additinoally add a HOLDLOCK to your 'with' statement so that the lock gets held a little longer.
This is treated quite well in this Answer
When i try to insert/update something in a db table, will Oracle lock the whole table or only the row being inserted/updated?
Is this something that can be controlled through external configuration?
We can issue locks explicitly with the LOCK TABLE command. Find out more
Otherwise, an insert does not lock any other rows. Because of Oracle's read isolation model that row only exists in our session until we commit it, so nobody else can do anything with it. Find out more.
An update statement only locks the affected rows. Unless we have implemented a pessimistic locking strategy with SELECT ... FOR UPDATE. Find out more.
Finally, in Oracle writers do not block readers. So even locked rows can be read by other sessions, they just can't be changed. Find out more.
This behaviour is baked into the Oracle kernel, and is not configurable.
Justin makes a good point about the table-level DDL lock. That lock will cause a session executing DDL on the table to wait until the DML session commits, unless the DDL is something like CREATE INDEX in which case it will fail immediately with ORA-00054.
It depends what you mean by "lock".
For 99.9% of what people are likely to care about, Oracle will acquire a row-level lock when a row is modified. The row-level lock still allows readers to read the row (because of multi-version read consistency, writers never block readers and readers never do dirty reads).
If you poke around v$lock, you'll see that updating a row also takes out a lock on the table. But that lock only prevents another session from doing DDL on the table. Since you'd virtually never want to do DDL on an active table in the first place, that generally isn't something that would actually cause another session to wait for the lock.
When a regular DML is executed (UPDATE/DELETE/INSERT,MERGE, and SELECT ... FOR UPDATE) oracle obtains 2 locks.
Row level Lock (TX) - This obtains a lock on the particular row being touched and any other transaction attempting to modify the same row gets blocked, till the one already owning it finishes.
Table Level Lock (TM) - When Row lock (TX) is obtained an additional Table lock is also obtained to prevent any DDL operations to occur while a DML is in progress.
What matters is though in what mode the Table lock is obtained.
A row share lock (RS), also called a subshare table lock (SS), indicates that the transaction holding the lock on the table has locked rows in the table and intends to update them. An SS lock is the least restrictive mode of table lock, offering the highest degree of concurrency for a table.
A row exclusive lock (RX), also called a subexclusive table lock (SX), indicates that the transaction holding the lock has updated table rows or issued SELECT ... FOR UPDATE. An SX lock allows other transactions to query, insert, update, delete, or lock rows concurrently in the same table. Therefore, SX locks allow multiple transactions to obtain simultaneous SX and SS locks for the same table.
A share table lock (S) held by one transaction allows other transactions to query the table (without using SELECT ... FOR UPDATE) but allows updates only if a single transaction holds the share table lock. Multiple transactions may hold a share table lock concurrently, so holding this lock is not sufficient to ensure that a transaction can modify the table.
A share row exclusive table lock (SRX), also called a share-subexclusive table lock (SSX), is more restrictive than a share table lock. Only one transaction at a time can acquire an SSX lock on a given table. An SSX lock held by a transaction allows other transactions to query the table (except for SELECT ... FOR UPDATE) but not to update the table.
An exclusive table lock (X) is the most restrictive mode of table lock, allowing the transaction that holds the lock exclusive write access to the table. Only one transaction can obtain an X lock for a table.
You should probably read the oracle concepts manual regarding locking.
For standard DML operations (insert, update, delete, merge), oracle takes a shared DML (type TM) lock.
This allows other DMLs on the table to occur concurrently (it is a share lock.)
Rows that are modified by an update or delete DML operation and are not yet committed will have an exclusive row lock (type TX). Another DML operation in another session/transaction can operate on the table, but if it modifies the same row it will block until the holder of the row lock releases it by either committing or rolling back.
Parallel DML operations and serial insert direct load operations take exclusive table locks.
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