我使用的是隔离级别的Microsoft SQL Server 2005数据库READ_COMMITTED
和READ_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:
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