Проблема с уровнем изоляции транзакций SQL

У меня проблема с уровнями изоляции транзакций. Здесь задействованы две таблицы,первый один часто обновляется с уровнем изоляции транзакции установлен в сериализуемый, the второй один имеет внешний ключ на первый один.

проблема возникает при выполнении вставки или обновления второй таблица. Раз в несколько часов я получаю сообщение об ошибке:

транзакции в режиме изоляции моментального снимка прерывается из-за конфликта обновлений. Вы не можете использовать изоляцию моментальных снимков для доступа к таблице dbo.сначала "прямо или косвенно в базе данных" DB " для обновления, удаления или вставки строки, которая была изменена или удалена другой транзакцией. Повторите попытку транзакции или измените уровень изоляции инструкции update/delete.

Я не устанавливаю уровень изоляции транзакций при вставке или обновлении второй таблица, также я запустил команду DBCC USEROPTIONS и он возвращает read_committed

Мне нужно устранить эту ошибку как можно скорее, спасибо вперед

2 ответов


первый:
Кажется, вы не используете SERIALIZABLE, но изоляция моментальных снимков, которая была введена с MSSQL 2005. Вот статья, чтобы понять difference:
http://blogs.msdn.com/b/craigfr/archive/2007/05/16/serializable-vs-snapshot-isolation-level.aspx

=> это было основано на ошибке, сообщении, но, как вы объяснили снова в комментариях, ошибка возникает при редактировании второй таблицы.

второй:
Для модификации MSSQL Server всегда пытается получить блокировки, и так как есть блокировки (с помощью транзакции) на первый таблица, которая перерастает в блокировки на второй таблица из-за (внешнего ключа) операция завершается неудачно. Таким образом, каждая модификация вызывает фактически мини-транзакцию.

уровень транзакции по умолчанию на MSSQL -READ COMMITTED, а если вы включите опцию READ_COMMITTED_SNAPSHOT он преобразует READ COMMITTED до SNAPSHOT как сделки каждый раз, когда вы использовать READ COMMITTED. Что приводит к появлению сообщения об ошибке.

если быть точным, как VladV указал, что на самом деле он не использует SNAPSHOT уровень изоляции, но READ COMMITTED при управлении версиями строк вместо блокировки, но только на сообщении основе, где SNAPSHOT использует ряд версий сделки основы.

чтобы понять разницу проверить this:
http://msdn.microsoft.com/en-us/library/ms345124(SQL.90).aspx

чтобы узнать больше о READ_COMMITTED_SNAPSHOT, свое объясненное подробно here:
http://msdn.microsoft.com/en-us/library/tcbchxcb(VS.80).aspx
и здесь: изменения уровня изоляции SQL Server по умолчанию

еще одна причина для вас, чтобы увидеть SNAPSHOT изоляция если вы не указали его, то с помощью неявной транзакции. После включения этой опции, и вы фактически не указываете уровень изоляции в инструкции modifying (чего вы не делаете), MS SQL server выберет то, что он считает право уровень изоляции. Здесь details:
http://msdn.microsoft.com/en-us/library/ms188317(SQL.90).aspx

для всех сценариев тезисов решение одно и то же.

устранение:
Необходимо выполнить операции в последовательности, и вы может сделать это специально использование транзакции с SERIALIZABLE уровень изоляции на обе операции: при вставке / обновлении первого и при вставке / обновлении второго.
Таким образом, вы блокируете соответствующий другой, пока он не будет завершен.


у нас была аналогичная проблема - и Вы были бы рады узнать, что вы должны быть в состоянии решить проблему, не удаляя ограничение FK.

в частности, в нашем сценарии у нас были частые обновления родительской таблицы в транзакции с фиксацией чтения. У нас также были частые параллельные (длительные) транзакции моментальных снимков, необходимые для вставки строк в дочернюю таблицу с FK в родительскую таблицу , поэтому по существу это тот же сценарий, что и Ваш, за исключением того, что мы использовали чтение Совершено вместо сделки SEREALIZABLE.

чтобы решить проблему, создайте новое уникальное НЕКЛАСТЕРИЗОВАННОЕ ограничение в основной таблице над столбцом FK. Кроме того, вы также должны повторно создать FK после создания уникального ограничения, так как это гарантирует, что FK теперь ссылается на ограничение (не кластеризованный ключ).

Примечание: недостатком является то, что теперь у вас есть кажущееся избыточным ограничение на таблицу, которая должна поддерживаться SQL сервер при обновлении родительской таблицы. Тем не менее, это может быть хорошей возможностью для вас рассмотреть другой/альтернативный кластеризованный ключ...и если Вам повезет, это может даже заменить потребность в другом индексе на этой таблице...

к сожалению, я не могу найти хорошее объяснение в интернете, почему создание уникального ограничения решает проблему. Самый простой способ объяснить, почему это работает, заключается в том, что 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