sql server update时,默认使用是行锁还是表锁?

https://bbs.csdn.net/topics/120000749

http://www.cnblogs.com/s021368/articles/2148659.html

问题:

udpate  a  
set   column1   =   1  
where   idx   =   1
sqlserver   执行update语句的时候,是锁整张表的吧

 

分析:

看表结构,   如果没有主键无法只锁定行

如果楼主要验证的话,   只需要类似下面的方法就行了:

--   开事务,   以保持锁
BEGIN   TRAN

--   更新
update  a  
set   column1   =   1  
where   idx   =   1

--   列出锁信息
EXEC   sp_lock   @@spid

--   提交或者回滚事务
COMMIT/ROLLBACK   TRAN

输出的结果大致是这样:

通过   dbid,   ObjId   可以找到你更新的表相关的锁记录
如果   IndId   为   0   ,   表示锁在表上,   否则在对应的索引上
通过   Type   列,   可以确定被锁定的是行/表,   或者是其他,   并且可以通过   Mode   看到是什么锁
在Status   中,   还可以看到锁是已经加上了,   还是在等待其他资源释放(以取得加锁的权利)

spid       dbid       ObjId               IndId     Type   Resource                                       Mode           Status
------   ------   -----------   ------   ----   --------------------------   --------   ------
53           1             1115151018     0             TAB                                                           IS               GRANT

 

锁的类型(Tyep   列值,   RID   和   KEY   的话,   表示锁在行上)   有如下几种:
RID   =   表中单个行的锁,由行标识符   (RID)   标识。
KEY   =   索引内保护可串行事务中一系列键的锁。
PAG   =   数据页或索引页的锁。
EXT   =   对某区的锁。
TAB   =   整个表(包括所有数据和索引)的锁。
DB   =   数据库的锁。
FIL   =   数据库文件的锁。
APP   =   指定的应用程序资源的锁。
MD   =   元数据或目录信息的锁。
HBT   =   堆或   B   树索引的锁。在   SQL   Server   2005   中此信息不完整。
AU   =   分配单元的锁。在   SQL   Server   2005   中此信息不完整。

 

其它:

SELECT   *   FROM   table   WITH   (HOLDLOCK)   其他事务可以读取表,但不能更新删除  

SELECT   *   FROM   table   WITH   (TABLOCKX)   其他事务不能读取表,更新和删除

 

Default SQL Server 2008 locking behaviour

问题

I am using SQL Server 2008.

  • What is the default lock behaviour with UPDATE?

    • NOLOCK?
    • ROWLOCK?
    • PAGLOCK?
  • How can I tell what the current level of lock is for a table?

Thanks, Bruce

 

回答1

It really depends on how much you're updating. Locks will escalate as the size of the query increases. If many rows within the same page will be modified, SQL Server will escalate to a PAGLOCK. If many pages will be modified, it will escalate to a TABLOCK.

评论:

Slight correction: row/key locks are never escalated to page locks. They are only escalated directly to table locks. However, the database engine may mix row and page locks for a single statement. msdn.microsoft.com/en-us/library/ms184286.aspx
– db2
Dec 15 '11 at 18:44

 

 

When I update/insert a single row should it lock the entire table?

问题

I have two long running queries that are both on transactions and access the same table but completely separate rows in those tables. These queries also perform some update and inserts based on those queries.

It appears that when these run concurrently that they encounter a lock of some kind and it’s preventing the task from finishing and locks up when it goes to update one of the rows. I’m using an exclusive row lock on the rows being read and the lock that shows up on the process is a lck_m_ix lock.

Two questions:

  1. When I update/insert a single row does it lock the entire table?
  2. What can be done to work around this sort of issue?

 

 回答1

Typically no, but it depends (most often used answer for SQL Server!)

SQL Server will have to lock the data involved in a transaction in some way. It has to lock the data in the table itself, and the data any affected indexes, while you perform a modification. In order to improve concurrency, there are several "granularities" of locking that the server might decide to use, in order to allow multiple processes to run: row locks, page locks, and table locks are common (there are more). Which scale of locking is in play depends on how the server decides to execute a given update. Complicating things, there are also classifications of locks like shared, exclusive, and intent exclusive, that control whether the locked object can be read and/or modified.

It's been my experience that SQL Server mainly uses page locks for changes to small portions of tables, and past some threshold will automatically escalate to a table lock, if a larger portion of a table seems (from stats) to be affected by an update or delete. The idea is that it is faster to lock a table (one lock) than obtaining and managing thousands of individual row or page locks for a big update.

To see what is happening in your specific case, you'd need to look at the query logic and, while your stuff is running, examine the locking/blocking conditions in sys.dm_tran_locks, sys.dm_os_waiting_tasks or other DMV's. You would want to discover what exactly is getting locked by what step in each of your processes, to discover why one is blocking the other.

 

回答2

The short version:

  1. No
  2. Fix your code.

The long version:

LCK_M_IX is an intent lock, meaning the operation will place an X lock on a subordinate element. Eg. When updating a row in a table, the operation table takes an IX lock on the table before locking X the row being updated/inserted/deleted. Intent locks are common strategy to deal with hierarchies, like table/page/row, because the lock manager cannot understand the physical structure of resources requested to be locked (ie. it cannot know that an X-lock on page P1 is incompatible with an S-lock on row R1 because R1 is contained in P1). For more details, see Lock Modes.

The fact that you are seeing contention on intent locks means you are trying to obtain high level object locks, like table locks. You will need to analyze your source code for the request being blocked (the one requesting the lock incompatible with LCK_M_IX) and remove the cause of the object level lock request. What that means will depend on your source code, I cannot know what you're doing there. My guess is that you use an erroneous lock hint.

A more general approach is to rely on SNAPSHOT ISOLATION. But this, most likely, will not solve the problem you're seeing, since snapshot isolation can only benefit row level contention issues, not applications that request table locks.

 

回答3

A frequent aim of using transactions: keep them as short and sweet as possible. I get the sense from your wording in the question that you are opening a transaction, then doing all kinds of things, some of which take a long time. Then expecting multiple users to be able to run this same code concurrently. Unfortunately, if you perform an insert at the beginning of that set of code, then do 40 other things before committing or rolling back, it is possible that that insert will block everyone else from running the same type of insert, essentially turning your operation from free-for-all to serial.

Find out what each query is doing, and if you are getting lock escalations that you wouldn't expect. Just because you say WITH (ROWLOCK) on a query doesn't mean SQL Server will be able to comply... if you are touched multiple indexes, indexed views, persisted computed columns etc. then there are all kinds of reasons why your rowlock may not hold any water. You also might have things later in the transaction that are taking longer than you think, and maybe you don't realize that the locks on all of the objects involved in the transaction (not just the statement that is currently running) can be held for the duration of the transaction.

 

When does sql exclusively lock a row in an update statement?

问题

Can a race condition occur in sql under these conditions?

If I have this SQL update running in one thread call it statement 1:

Update Items
Set Flag = B
where Flag = A;

And this SQL update running in another call it statement 2:

Update Items
Set Flag = C
where Flag = A;

Is it possible for each thread to read the same record where Flag is equal to A and write the record with their own values? Such that statement 1 can write it first and then statement 2 writes it or visa versa?

The answer to this question depends on when the database exclusively locks the update. Does it happen before it finds the records or after it finds the records and evaluates the where clause?

 

回答1

First, there are three lock contexts:

  • Database level lock
  • Table level lock
  • Row level lock

Then you have four lock modes:

  • IX
  • IS
  • X
  • S

IX and IS locks are "intention" locks. These locks are held before acquiring other types of locks. X locks are exclusive (write) locks and S locks are shared (read) locks.

The locks (IX,IS,X or S) locks can be taken at any context level. An X lock at the database level will block all other operations in the database for example. This is the type of lock that SQLlite takes. An S lock is taken for the entire database during reads, and an X lock is taken for the entire database during writes. Writes will wait for any S locks to complete and will block new S and X locks until the write lock is released. This provides a serializable isolation transaction level.

For MySQL, the locking depends on the storage engine. MyISAM will take X and S locks on entire (sets of) tables. X locks will wait on existing S or X locks and block new locks. New X locks will be given higher priority in the queue, moved ahead of new S locks. This behavior can be changed by setting LOW_PRIORITY_UPDATES, which could result in write starvation because writes will be de-prioritized in favor of reads.

It is possible in MySQL to obtain an X lock over the entire database using 'FLUSH TABLES WITH READ LOCK'.

InnoDB locks rows as they are encountered via an index read. InnoDB locks index records and locks the records when the index records are traversed. InnoDB uses special locks called 'gap' locks to ensure REPEATABLE-READ transaction isolation level. Locks are held on index entries, so if a table is not well indexed for an UPDATE query, then many rows will be locked. Note that InnoDB does not create S locks for normal SELECT queries. It uses row versioning, not row level locking for consistent snapshots.

When acquiring X locks, the database needs to detect deadlocks. Consider the following:

>connection 1
start transaction;
update T set c = c + 1 order by id asc;

>connection 2
start transaction;
update T set c = c - 1 order by id desc;

In a row locking model, these two statements can not both complete successfully. The first would wait forever to acquire locks the second holds, and vice-versa. The database will pick one of the connections to roll back. InnoDB will pick the connection which has made the fewest number of changes. MyISAM will lock the whole table for whichever connection acquires the lock first, and then the second will run after the first completes.

The simple example given by you will be resolved by X locks at any context (database, table or row). If two connections begin at exactly the same type, both running two updates which try to update the same row, both will attempt to acquire an X lock. Only one connection can acquire the X lock. It is not possible to determine exactly which one will acquire the lock. The other connection will have to wait until the lock is released until it can acquire the X lock. Keep in mind, that if the row was locked by a DELETE or UPDATE, then the waiter might end up not acquiring a lock after waiting, because there is nothing left in the database to lock.

In your example, the first UPDATE to acquire the X lock, and the second UPDATE will then wait on the X lock and will eventually execute but not match any rows.

 

回答2

All SQL databases pretty much guarantee that such a collision will not occur. "When" locking occurs depends on whether locking is at the table, partition, page, or row level. Or, whether you have turned off such locking in your database.

What can happen, if you have concurrent update statements and multiple rows being updated, is that sone row are updated with the first, some with the second.

In general, I think of the where clause as being evaluated to select the row set, lock the rows one at a time, do the update and unlock. However, this depends on the type of locking. In this case, the scenario above would continue with the values flipping.

If you are concerned about this situation, use table level locking to force serialization when concurrent update requests are being processed.

 

 

 

posted @ 2018-12-26 11:43  ChuckLu  阅读(10757)  评论(2编辑  收藏  举报