7

トランザクション分離レベルに関連するエラーメッセージが表示されます。関連するテーブルは2つあり、最初のテーブルはトランザクション分離レベルがSERIALIZABLEに設定されて頻繁に更新され、 2番目のテーブルは最初のテーブルに外部キーを持っています。

2番目のテーブルの挿入または更新を行うときに問題が発生します。数時間に1回、次のエラーメッセージが表示されます。

更新の競合が原因でスナップショットアイソレーショントランザクションが中止されました。スナップショットアイソレーションを使用して、データベース'DB'のテーブル'dbo.first'に直接または間接的にアクセスして、別のトランザクションによって変更または削除された行を更新、削除、または挿入することはできません。トランザクションを再試行するか、更新/削除ステートメントの分離レベルを変更してください。

2番目のテーブルを挿入または更新するときにトランザクション分離レベルを設定しません。また、コマンドDBCC USEROPTIONSを実行すると、read_committedが返されます。

4

2 に答える 2

7

まず
、使用していないようですがSERIALIZABLE、MSSQL 2005で導入されたスナップショットアイソレーションです。違いを理解するための記事は次のとおりです。http:
//blogs.msdn.com/b/craigfr/archive/2007/05/ 16 / serializable-vs-snapshot-isolation-level.aspx

=>これはエラー、メッセージに基づいていましたが、コメントで再度説明したように、2番目のテーブルを編集するときにエラーが発生します。

2番目:変更の場合、MSSQL Serverは常にロックを取得しようとします。最初の
テーブル には(トランザクションを使用して)ロックがあり、(外部キー)のために2番目のテーブルのロックにエスカレートするため、操作は失敗します。したがって、すべての変更により、実際にはミニトランザクションが発生します。

MSSQLのデフォルトのトランザクションレベルはですがREAD COMMITTED、このオプションをオンにすると、を使用するたびに同様のトランザクションにREAD_COMMITTED_SNAPSHOT変換READ COMMITTEDされます。その後、エラーメッセージが表示されます。 SNAPSHOTREAD COMMITTED

VladVが指摘したように、正確には、SNAPSHOT分離レベル を実際に使用しているのではなく、ロックではなく行のバージョン管理を使用していますが、トランザクションベースで行のバージョン管理を使用しているステートメントベースでのみREAD COMMITTED 使用ます。SNAPSHOT

違いを理解するには、次を確認してください:http:
//msdn.microsoft.com/en-us/library/ms345124 (SQL.90).aspx

の詳細については、http: //msdn.microsoft.com/en-us/library/tcbchxcb (VS.80).aspx およびここ READ_COMMITTED_SNAPSHOTで詳しく説明されています:デフォルトのSQLServerIsolationLevelの変更

SNAPSHOT指定していない場合に分離が表示されるもう1つの理由は、暗黙的なトランザクションを使用することです。このオプションをオンにした後、変更ステートメントで分離レベルを実際に指定しない場合(指定しない場合)、MSSQLサーバーは適切な分離レベルであると彼が信じるものを選択します。詳細は次のとおりです。http:
//msdn.microsoft.com/en-us/library/ms188317 (SQL.90).aspx

ただし、これらすべてのシナリオで、ソリューションは同じです。

解決策:
操作を順番に実行する必要があります。これは、最初の操作の挿入/更新時と2番目の操作の挿入/更新時の両方の操作で分離レベルのトランザクションを具体的に使用することで実行できます。SERIALIZABLE
このようにして、完了するまでそれぞれをブロックします。

于 2011-02-24T09:15:58.233 に答える
5

同様の問題が発生しました。FK制約を削除しなくても問題を解決できるはずです。

具体的には、このシナリオでは、READCOMMITTEDトランザクションで親テーブルが頻繁に更新されました。また、親テーブルへのFKを使用して子テーブルに行を挿入する必要がある同時(長時間実行)スナップショットトランザクションが頻繁に発生しました。したがって、SEREALIZABLEトランザクションの代わりにREAD COMMITTEDを使用したことを除いて、基本的には同じシナリオです。

この問題を解決するには、FK列のプライマリテーブルに新しいUNIQUENONCLUSTERED制約を作成します。さらに、一意の制約を作成した後でFKを再作成する必要があります。これにより、FKが(クラスター化されたキーではなく)制約を参照するようになります。

注:欠点は、親テーブルが更新されたときにSQLサーバーによって維持される必要があるテーブルに一見冗長な制約があることです。そうは言っても、別の/代替のクラスター化されたキーを検討する良い機会かもしれません...そして運が良ければ、このテーブルの別のインデックスの必要性を置き換えることさえできます...

残念ながら、一意の制約を作成することで問題が解決する理由について、Web上で適切な説明を見つけることができません。これが機能する理由を説明する最も簡単な方法は、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

上記のシナリオを修正するには、テストデータベースを再セットアップします。次に、テスト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
于 2015-11-11T03:48:48.420 に答える