选择具有SQL服务器更新(SELECT FOR UPDATE with SQL Server)

2019-07-19 18:55发布

我使用的是隔离级别的Microsoft SQL Server 2005数据库READ_COMMITTEDREAD_COMMITTED_SNAPSHOT=ON

现在我想用:

SELECT * FROM <tablename> FOR UPDATE

...所以说当试图访问同一排“FOR UPDATE”其他数据库连接块。

我试过了:

SELECT * FROM <tablename> WITH (updlock) WHERE id=1

...但是这块甚至比选择“1”以外的ID的所有其他连接。

这是正确的提示做一个SELECT FOR UPDATE已知的Oracle,DB2,MySQL的?

编辑2009-10-03:

这些都是创建表和索引的语句:

CREATE TABLE example ( Id BIGINT NOT NULL, TransactionId BIGINT, 
    Terminal BIGINT, Status SMALLINT );
ALTER TABLE example ADD CONSTRAINT index108 PRIMARY KEY ( Id )
CREATE INDEX I108_FkTerminal ON example ( Terminal )
CREATE INDEX I108_Key ON example ( TransactionId )

很多并行进程做SELECT

SELECT * FROM example o WITH (updlock) WHERE o.TransactionId = ?

编辑2009-10-05:

为了更好地概述我写下来下表中的所有尝试的解决方案:

mechanism              | SELECT on different row blocks | SELECT on same row blocks
-----------------------+--------------------------------+--------------------------
ROWLOCK                | no                             | no
updlock, rowlock       | yes                            | yes
xlock,rowlock          | yes                            | yes
repeatableread         | no                             | no
DBCC TRACEON (1211,-1) | yes                            | yes
rowlock,xlock,holdlock | yes                            | yes
updlock,holdlock       | yes                            | yes
UPDLOCK,READPAST       | no                             | no

I'm looking for        | no                             | yes

Answer 1:

最近,我有一个死锁问题 ,因为SQL Server锁定更多的则需要(页)。 你真的不能做什么反对意见。 现在,我们正在追赶僵局例外......我希望我有甲骨文代替。

编辑:我们正在使用快照隔离的同时,解决了许多,但不是所有的问题。 不幸的是,要能够使用快照隔离,必须由数据库服务器,这可能导致在客户现场不必要的问题被允许。 现在,我们不仅捕获死锁异常(这仍然会发生,当然),而且快照的并发问题重复从后台进程交易(不能被用户重复)。 但是,这仍然执行比以前好多了。



Answer 2:

我有一个类似的问题,我想只锁定1行。 据我所知,有UPDLOCK选项,SQLSERVER锁所有的,它需要为了得到该行读取行。 所以,如果你没有定义一个索引直接访问行,所有的行前将被锁定。 在您的例子:

Asume,你有一个名为TBL与表id字段。 要锁定与行id=10 。 您需要定义字段ID的指数(或参与任何其他领域,你选择):

CREATE INDEX TBLINDEX ON TBL ( id )

然后,您的查询只锁定你读的是行:

SELECT * FROM TBL WITH (UPDLOCK, INDEX(TBLINDEX)) WHERE id=10.

如果不使用INDEX(TBLINDEX)选项,SQLSERVER需要从表的开始读取所有的行以找到你的行id=10 ,因此这些行会被锁定。



Answer 3:

你不能有快照隔离和阻断在同一时间读取。 快照隔离的目的是为了防止阻塞读取。



Answer 4:

尝试(UPDLOCK,ROWLOCK)



Answer 5:

完整的答案可以深入到DBMS的内部结构。 这取决于如何查询引擎(其执行由SQL优化器生成的查询计划)进行操作。

然而,一个可能的解释(适用于某些DBMS的至少一些版本-不一定是MS SQL Server)的是,有在ID列上没有索引,因此任何试图处理工作与'查询WHERE id = ? “在它结束了做表的顺序扫描,并连续扫描击中,你的应用程序锁。 您还可以遇到问题如果DBMS默认应用页级锁; 锁定一个行锁整个页面和页面上的所有行。

有一些,你可以揭穿这是麻烦的来源途径。 看看查询计划; 研究指标; 试试你的选择具有100万而不是1号,看看其它进程是否仍然受阻。



Answer 6:

也许使MVCC永久可以解决这个问题(而不是只有特定批次:SET事务隔离级别快照):

ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;

[编辑:10月14日]

:在阅读完之后在甲骨文于SQL Server更好的并发? 这: http://msdn.microsoft.com/en-us/library/ms175095.aspx

当READ_COMMITTED_SNAPSHOT数据库选项设置为ON,用于支持该选项的机制被立即激活。 当设置READ_COMMITTED_SNAPSHOT选项,则仅执行ALTER DATABASE命令的连接被允许在数据库中。 必须有数据库中没有其他打开的连接,直到ALTER DATABASE完成。 该数据库不必在单用户模式。

我来,你需要以永久激活MSSQL的MVCC一个给定的数据库上设置两个标志的结论:

ALTER DATABASE yourDbNameHere SET ALLOW_SNAPSHOT_ISOLATION ON;
ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;


Answer 7:

OK,默认情况下使用单一选择WIL“提交读”事务隔离哪些锁,因此停止写入该组。 您可以更改与事务隔离级别

Set Transaction Isolation Level { Read Uncommitted | Read Committed | Repeatable Read | Serializable }
Begin Tran
  Select ...
Commit Tran

这些详细的SQL Server BOL解释

你的下一个问题是,默认情况下,SQL Server的2K5就会升级锁,如果你有超过〜2500门的锁或锁定事务中使用的“正常”的内存超过40%。 升级进入页面,则表锁

您可以通过设置“跟踪标志”1211吨关掉这个升级了,看到BOL更多信息



Answer 8:

我假设你不希望任何其他会议,能够同时这个特定的查询正在运行读取行...

而使用WITH(XLOCK,READPAST)锁定提示会得到你想要的结果你包裹在一个事务中SELECT。 只要确保不使用WITH(NOLOCK)与其他并发读取。 READPAST允许其他会话执行相同的SELECT但在其他行。

BEGIN TRAN
  SELECT *
  FROM <tablename> WITH (XLOCK,READPAST) 
  WHERE RowId = @SomeId

  -- Do SOMETHING

  UPDATE <tablename>
  SET <column>=@somevalue
  WHERE RowId=@SomeId
COMMIT


Answer 9:

应用程序锁是推出自己的锁定与定制的粒度,同时避免“有帮助的”锁升级的一种方式。 见sp_getapplock 。



Answer 10:

创建一个假的更新执行ROWLOCK。

UPDATE <tablename> (ROWLOCK) SET <somecolumn> = <somecolumn> WHERE id=1

如果这不是你的锁定行,天知道会怎样。

这种“后UPDATE ”你可以做你的SELECT (ROWLOCK)和后续更新。



Answer 11:

尝试使用:

SELECT * FROM <tablename> WITH ROWLOCK XLOCK HOLDLOCK

这应该使锁独占,并保持了交易的持续时间。



Answer 12:

根据这篇文章 ,解决的办法是使用WITH(REPEATABLEREAD)提示。



Answer 13:

重温您所有的疑问,也许你有一些查询/是选择不ROWLOCK从你的SELECT FOR UPDATE同桌更新提示。


MSSQL经常升级那些行锁到页级锁(甚至表级锁,如果没有您所查询字段索引),看到这个解释 。 既然你问FOR UPDATE,我可以假设你需要transacion级(如财务,库存等)的鲁棒性。 因此,在该网站上的意见并不适用于您的问题。 这只是一个洞察为什么MSSQL 升级锁


如果你已经在使用MSSQL 2005年(及以上),它们都是基于MVCC,我想你应该有使用ROWLOCK / UPDLOCK提示行级锁没有问题。 但是,如果你已经在使用MSSQL 2005年及以上者,尝试检查你的一些查询其查询你想同样的表来FOR UPDATE,如果他们通过检查他们的WHERE子句中的字段,如果他们有指数升级锁。


PS
我使用PostgreSQL,它也采用MVCC有FOR UPDATE,我不会遇到同样的问题。 锁升级是什么MVCC解决了,所以我会感到惊讶,如果2005年仍然MSSQL与升级WHERE子句不对其领域指数表锁。 如果(锁升级)仍然是MSSQL 2005的情况下,尝试检查WHERE子句中的字段,如果他们有指标。

免责声明:我最后一次使用MSSQL的是2000版本只。



Answer 14:

你必须处理异常的提交时间和重复交易。



Answer 15:

问题 - 是这种情况下被证明是锁升级的结果(即如果用探查器中进行锁升级事件的跟踪,是肯定发生了什么事,以引起堵塞的)? 如果是这样,有一个完整的解释,并通过实例级别启用跟踪标记,以防止锁升级(相当极端)的解决方法。 见http://support.microsoft.com/kb/323630跟踪标志1211

但是,这可能会产生意想不到的副作用。

如果你是故意锁住一行并保持锁定较长时间,然后使用事务的内部锁定机制是不是最好的方法(在SQL Server至少)。 在SQL Server中的所有优化是面向对短期交易 - 获得,进行更新,全身而退。 这对在首位锁升级的原因。

因此,如果意图是在长时间内以“退房”的行,而不是交易锁定,最好使用带有值的列和纯醇”更新语句来标志行作为锁止。



Answer 16:

我在一个完全不同的方式解决了这个问题ROWLOCK。 我意识到,SQL服务器无法以令人满意的方式来管理这样的锁。 我选用通过使用互斥... waitForLock ... RELEASELOCK的从一个编程角度解决这个问题?



Answer 17:

您是否尝试过READPAST?

我已经处理表就像一个队列时使用UPDLOCK和READPAST在一起。



Answer 18:

如何试图做此行上简单的更新第一(没有真正改变任何数据)? 你可以像在该行进行后,选择要更新。

UPDATE dbo.Customer SET FieldForLock = FieldForLock WHERE CustomerID = @CustomerID
/* do whatever you want */

编辑 :你应该把它包在课程的交易

编辑2:另一种解决方案是使用SERIALIZABLE隔离级别



文章来源: SELECT FOR UPDATE with SQL Server