3

There is a process that runs ever hour that has to insert into table1 but when the query below is running, the inserts are blocked. Can anyone explain why? I realize that the lock hints are just that, hints and SQL may chose to ignore them. table1 has 300m rows and I need to update the values in ColumnA. I'm doing it in chunks to help with the rollback in case the process has to be stopped, hence the while loop.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
go

declare @start bigint
declare @end bigint
declare @max bigint
declare @step int

set @step = 50000
set @start = 17953095
set @end = @start + @step
set @max = @start + 2000000

while ( @end < @max )
begin

waitfor delay '00:00:10'

begin transaction

update [table1] with (ROWLOCK)
set [table1].[ColumnA] = [table2].[ColumnA]
from [table2] (nolock)
where [table2].[ColumnB] = [table1].[ColumnB] 
and [table1].ID >= @start 
and [table1].ID <  @end

commit transaction

print @end

if @end >= @max 
begin
    break
end

set @start = @end
set @end = @end + @step

end

print @end

Can I do this another way so it doesn't block the inserts or other updates? Nothing else will need to work with ColumnA other than my query above.

ILovePaperTowels
  • 1,445
  • 2
  • 17
  • 30

1 Answers1

2

You can try using a smaller batch size. SQL Server escalates row locks straight to table locks when a certain threshold is exceeded, per MSDN:

The Database Engine does not escalate row or key-range locks to page locks, but escalates them directly to table locks.
...
...
Lock escalation is triggered when lock escalation is not disabled on the table by using the ALTER TABLE SET LOCK_ESCALATION option, and when either of the following conditions exists:

A single Transact-SQL statement acquires at least 5,000 locks on a single nonpartitioned table or index.

A single Transact-SQL statement acquires at least 5,000 locks on a single partition of a partitioned table and the ALTER TABLE SET LOCK_ESCALATION option is set to AUTO.

The number of locks in an instance of the Database Engine exceeds memory or configuration thresholds.

If locks cannot be escalated because of lock conflicts, the Database Engine periodically triggers lock escalation at every 1,250 new locks acquired.

Your current batch size of 50,000 is well in excess of that threshold.

The other thing to think about is whether you have suitable indices in place to suit your WHERE clause - make sure that is as optimal as possible.

AdaTheDev
  • 142,592
  • 28
  • 206
  • 200
  • So you think reducing to 5k step will help? Can I see if a table lock was taken out on the table? – ILovePaperTowels Nov 23 '11 at 20:36
  • +1 right on - do more than 5'000 updates on a table, and you got yourself an exclusive (X) table lock! – marc_s Nov 23 '11 at 20:37
  • Or should I change the lock escalation? – ILovePaperTowels Nov 23 '11 at 20:37
  • Agreed, this is most likely a lock escalation issue. You can't get out of needing a lock completely (it's _required_ for the update), but you may be able to decrease the duration; you don't appear to have anything that logically requires a transaction. DB2 has a `NO COMMIT` or `NC` lock level that basically means that rows aren't locked after they're accessed for update/insert/delete (and effectively never for selects) - does SQL Server have something similar? – Clockwork-Muse Nov 23 '11 at 20:39
  • @ILovePaperTowels - I would try lowering the updates to < 5000 as otherwise you'd be disabling lock escalation at the table level, which is a far bigger decision to make – AdaTheDev Nov 23 '11 at 20:48
  • I turned it down to 4999 and I don't see any locks for table1 using the query from http://stackoverflow.com/questions/599453/find-locked-table-in-sql-server but it is taking a lot longer to run the query – ILovePaperTowels Nov 23 '11 at 20:52
  • @ILovePaperTowels: unfortunately, you cannot change the limit on when the lock is escalated to the table level. There are options to turn it OFF (table by table) - but it's not recommended – marc_s Nov 23 '11 at 21:33
  • See [this blog post on lock escalation](http://blogs.msdn.com/b/sqlserverstorageengine/archive/2006/05/17/lock-escalation.aspx) or the [MSDN docs on trace flags (see TF 1211 and 1224)](http://msdn.microsoft.com/en-us/library/ms188396.aspx) on how to disable lock escalation - but again: **not recommended!** – marc_s Nov 23 '11 at 21:35
  • What about using snapshot isolation? Wouldn't that give better concurrency? –  Nov 23 '11 at 22:03