aro*_*ron 7 sql sql-server deadlock transaction-isolation
我有事务隔离级别的问题.这里涉及两个表,第一个被设置为事务隔离级别经常更新SERIALIZABLE时,第二一个有外键的第一个.
插入或更新第二个表时出现问题.几个小时后,我收到错误消息:
由于更新冲突,快照隔离事务中止.您不能使用快照隔离直接或间接访问数据库'DB'中的表'dbo.first'来更新,删除或插入已被另一个事务修改或删除的行.重试事务或更改update/delete语句的隔离级别.
插入或更新第二个表时我没有设置事务隔离级别,我也运行命令DBCC USEROPTIONS并返回read_committed
我需要尽快消除这个错误,谢谢你
第一:
看起来,你没有使用SERIALIZABLE,但是MSSQL 2005引入了快照隔离.这是一篇了解差异的文章:http:
//blogs.msdn.com/b/craigfr/archive/2007/05/ 16 /串行化-VS-快照隔离-level.aspx
=>这是基于错误消息,但正如您在评论中再次解释的那样,编辑第二个表时出现错误.
第二:
对于修改,MSSQL Server总是尝试获取锁,并且由于(外键)操作失败,因此第一个表上的锁(通过使用事务)升级到第二个表上的锁.因此,每次修改都会导致一个小型交易.
MSSQL上的默认事务级别是READ COMMITTED,但如果您打开该选项READ_COMMITTED_SNAPSHOT,它将READ COMMITTED在SNAPSHOT每次使用时转换为类似的事务READ COMMITTED.然后会导致您收到的错误消息.
确切地说,正如VladV指出的那样,它并不是真正使用SNAPSHOT隔离级别,而是 READ COMMITTED 使用行版本控制而不是锁定,但仅在语句基础上,在事务基础上SNAPSHOT使用行版本控制.
要了解差异,请查看:http:
//msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx
要了解有关READ_COMMITTED_SNAPSHOT它的更多信息,请在此处详细解释:
http://msdn.microsoft.com/en-us/library/tcbchxcb(VS.80).aspx
和此处:
默认SQL Server IsolationLevel更改
SNAPSHOT如果您没有指定隔离,则看到隔离的另一个原因是使用隐式事务.在打开此选项后,您实际上并未在修改语句(您没有)上指定隔离级别,MS SQL服务器将选择他认为是正确隔离级别的任何内容.以下是详细信息:http:
//msdn.microsoft.com/en-us/library/ms188317(SQL.90).aspx
对于所有这些场景,解决方案是相同的.
解决方案:
您需要按顺序执行操作,并且可以通过在两个操作上专门使用具有SERIALIZABLE隔离级别的事务来执行此操作:插入/更新第一个操作时以及插入/更新第二个操作时.
这样您就可以阻止相应的其他人,直到完成为止.
我们遇到了类似的问题 - 您很高兴知道您应该能够在不删除FK约束的情况下解决问题.
具体来说,在我们的场景中,我们在READ COMMITTED事务中经常更新父表.我们还经常发生并发(长时间运行)快照事务,需要将行插入带有FK到父表的子表中 - 所以基本上它与您的情况相同,除了我们使用READ COMMITTED而不是SEREALIZABLE事务.
要解决此问题,请在FK列上的主表上创建新的UNIQUE NONCLUSTERED约束.此外,您还必须在创建唯一约束后重新创建FK,因为这将确保FK现在引用约束(而不是聚簇键).
注意:缺点是您现在对表有一个看似冗余的约束,当对父表进行更新时,SQL Server需要维护该约束.也就是说,这可能是您考虑使用不同/备用群集密钥的好机会......如果您很幸运,它甚至可以替换此表中另一个索引的需求......
不幸的是,我无法在网上找到一个很好的解释为什么创建一个独特的约束来解决问题.我能解释其原因的最简单方法是因为FK现在只引用唯一约束 - 并且对父表的修改(即对非FK引用的列)不会导致快照事务中的更新冲突,因为FK现在引用未更改的唯一约束条目.将此与群集密钥进行对比,其中对父表中任何列的更改将影响此表中的行版本 - 并且由于FK看到更新的版本号,因此快照事务需要中止.
此外,如果在非快照事务中删除父行,则会影响聚簇和唯一约束,并且正如预期的那样,快照事务将回滚(因此保持FK完整性).
我已经能够使用我从这篇博客文章改编的上述示例代码重现此问题
---------------------- SETUP Test database
-- Creating Customers table without unique constraint
USE master;
go
IF EXISTS (SELECT * FROM sys.databases WHERE name = 'SnapshotTest')
BEGIN;
DROP DATABASE SnapshotTest;
END;
go
CREATE DATABASE SnapshotTest;
go
ALTER DATABASE SnapshotTest
SET ALLOW_SNAPSHOT_ISOLATION ON;
go
USE SnapshotTest;
go
CREATE TABLE Customers
(CustID int NOT NULL PRIMARY KEY,CustName varchar(40) NOT NULL);
CREATE TABLE Orders
(OrderID char(7) NOT NULL PRIMARY KEY,
OrderType char(1) CHECK (OrderType IN ('A', 'B')),
CustID int NOT NULL REFERENCES Customers (CustID)
);
INSERT INTO Customers (CustID, CustName) VALUES (1, 'First test customer');
INSERT INTO Customers (CustID, CustName) VALUES (2, 'Second test customer');
GO
---------------------- TEST 1: Run this test before test 2
USE SnapshotTest;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
-- Check to see that the customer has no orders
SELECT * FROM Orders WHERE CustID = 1;
-- Update the customer
UPDATE Customers SET CustName='Updated customer' WHERE CustID = 1;
-- Twiddle thumbs for 10 seconds before commiting
WAITFOR DELAY '0:00:10';
COMMIT TRANSACTION;
go
-- Check results
SELECT * FROM Customers (NOLOCK);
SELECT * FROM Orders (NOLOCK);
GO
---------------------- TEST 2: Run this test in a new session shortly after test 1
USE SnapshotTest;
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
BEGIN TRANSACTION;
SELECT * FROM Customers WHERE CustID = 1;
INSERT INTO Orders (OrderID, OrderType, CustID) VALUES ('Order01', 'A', 1);
-- Twiddle thumbs for 10 seconds before commiting
WAITFOR DELAY '0:00:10';
COMMIT TRANSACTION;
go
-- Check results
SELECT * FROM Customers (NOLOCK);
SELECT * FROM Orders (NOLOCK);
go
Run Code Online (Sandbox Code Playgroud)
要修复上述场景,请重新设置测试数据库.然后在运行测试1和2之前运行以下脚本.
ALTER TABLE Customers
ADD CONSTRAINT UX_CustID_ForSnapshotFkUpdates UNIQUE NONCLUSTERED (CustID)
-- re-create the existing FK so it now references the constraint instead of clustered index (the existing FK probably has a different name in your DB)
ALTER TABLE [dbo].[Orders] DROP CONSTRAINT [FK__Orders__CustID__1367E606]
ALTER TABLE [dbo].[Orders] WITH CHECK ADD FOREIGN KEY([CustID])
REFERENCES [dbo].[Customers] ([CustID])
GO
Run Code Online (Sandbox Code Playgroud)