使用SQL Server选择更新

tan*_*ens 76 sql t-sql sql-server sql-server-2005 read-committed-snapshot

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

现在我想用:

SELECT * FROM <tablename> FOR UPDATE
Run Code Online (Sandbox Code Playgroud)

...以便在尝试访问同一行"FOR UPDATE"时阻止其他数据库连接.

我试过了:

SELECT * FROM <tablename> WITH (updlock) WHERE id=1
Run Code Online (Sandbox Code Playgroud)

...但是即使选择"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 )
Run Code Online (Sandbox Code Playgroud)

很多并行进程都这样做SELECT:

SELECT * FROM example o WITH (updlock) WHERE o.TransactionId = ?
Run Code Online (Sandbox Code Playgroud)

编辑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

Ste*_*ger 33

最近我遇到了死锁问题,因为Sql Server会锁定更多必要的(页面).你无法真正做任何反对它.现在我们正在捕捉死锁异常......我希望我有Oracle.

编辑:我们同时使用快照隔离,这解决了很多但不是所有的问题.遗憾的是,为了能够使用快照隔离,数据库服务器必须允许它,这可能会在客户站点上造成不必要的问题.现在我们不仅捕获死锁异常(当然仍然可以发生),而且还捕获快照并发问题以从后台进程重复事务(用户不能重复).但这仍然比以前表现得更好.

  • @bjan:查找快照隔离.这里有一些信息http://msdn.microsoft.com/en-us/library/tcbchxcb(v=vs.80).aspx (2认同)

小智 19

我有类似的问题,我想只锁定1行.据我所知,使用UPDLOCK选项,SQLSERVER会锁定它需要读取的所有行以获取行.因此,如果您没有定义索引来直接访问该行,则所有前面的行都将被锁定.在你的例子中:

假设您有一个名为TBL的表,其中包含一个id字段.你想锁定行id=10.您需要为字段ID(或您选择的任何其他字段)定义索引:

CREATE INDEX TBLINDEX ON TBL ( id )
Run Code Online (Sandbox Code Playgroud)

然后,您的查询仅锁定您读取的行:

SELECT * FROM TBL WITH (UPDLOCK, INDEX(TBLINDEX)) WHERE id=10.
Run Code Online (Sandbox Code Playgroud)

如果您不使用INDEX(TBLINDEX)选项,SQLSERVER需要从表的开头读取所有行以查找您的行id=10,因此这些行将被锁定.


Chr*_*ter 8

您不能同时拥有快照隔离和阻止读取.快照隔离的目的是防止阻塞读取.


Blu*_*kMN 5

试试(updlock,rowlock)


Jon*_*ler 5

完整的答案可以深入研究DBMS的内部.它取决于查询引擎(执行SQL优化器生成的查询计划)的运行方式.

但是,一个可能的解释(适用于某些DBMS的至少某些版本 - 不一定适用于MS SQL Server)是ID列上没有索引,因此任何尝试使用' WHERE id = ?'进行查询的进程最终都会执行对表进行顺序扫描,顺序扫描会触发您的进程应用的锁.如果DBMS默认应用页面级锁定,也会遇到问题; 锁定一行会锁定整个页面以及该页面上的所有行.

有一些方法你可以揭穿这个麻烦的来源.查看查询计划; 研究指标; 尝试使用ID为1000000而不是1的SELECT,并查看是否仍然阻止了其他进程.


Mic*_*uen 5

或许使mvcc永久化可以解决它(而不是仅与特定批次相反:SET TRANSACTION ISOLATION LEVEL SNAPSHOT):

ALTER DATABASE yourDbNameHere SET READ_COMMITTED_SNAPSHOT ON;
Run Code Online (Sandbox Code Playgroud)

[编辑:10月14日]

阅读本文后:Oracle中的并发性比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;
Run Code Online (Sandbox Code Playgroud)