2010-04-26 6 views
2

Рассмотрите базу данных SQL Server и две сохраненные процедуры:SQL Server: предотвращение грязных чтений в хранимой процедуре

* 1. Прок, который выполняет 3 важные вещи в транзакции: создайте клиента, вызовите sproc для выполнения другой вставки и условно вставьте третью запись с новым идентификатором.

BEGIN TRAN 
    INSERT INTO Customer(CustName) (@CustomerName) 
    SELECT @NewID = SCOPE_IDENTITY() 

    EXEC CreateNewCustomerAccount @NewID, @CustomerPhoneNumber 

    IF @InvoiceTotal > 100000 
     INSERT INTO PreferredCust(InvoiceTotal, CustID) VALUES (@InvoiceTotal, @NewID) 

COMMIT TRAN 

* 2. Сохраненная процедура, которая опросает таблицу Customer для новых записей, которые не имеет, имеет соответствующую запись PreferredCust. Клиентское приложение выполняет опрос, вызывая этот хранимый процесс каждые 500 мс. SELECT на Customer НЕ включает транзакцию.

--not in the Preferred list 
    SELECT C.ID 
    FROM Customer AS C 
    LEFT JOIN PreferredCust AS PRE ON PRE.CustID = C.ID 
    WHERE PRE.CustID IS NULL 

Проблема возникла, когда процедура опроса хранится нашел запись в таблице Customer, и вернула его как часть его результатов. Проблема заключалась в том, что он взял эту запись, я предполагаю, как часть грязного чтения. Запись закончилась тем, что позже появилась запись в PreferredCust, и в итоге возникла проблема с нисходящим потоком.

Вопрос

  • Как вы можете явно предотвратить грязное чтение этой второй хранимая процедура?
  • Насколько вероятно мое предположение о сценарии грязного чтения?

Среда SQL Server 2005 с конфигурацией по умолчанию из коробки. Никакие другие блокирующие удары не указаны в любой из этих хранимых процедур.

Эти два хранимых procs вызываются с Java-клиента через соединение JDBC. Неизвестно, используют ли они одно и то же соединение, но SQL Profiler показывает, что они используют тот же SPID и ClientProcessID.

Вот что показывает SQL Profiler:

SELECT @@MAX_PRECISION 
SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
SET IMPLICIT_TRANSACTIONS OFF 
SET QUOTED_IDENTIFIER ON 
SET TEXTSIZE 2147483647 
go 
EXEC WriteNewCustomer 'CustomerX', 199000 
go 

--get any customers in the priority 
SELECT @@MAX_PRECISION 
SET TRANSACTION ISOLATION LEVEL READ COMMITTED 
SET IMPLICIT_TRANSACTIONS OFF 
SET QUOTED_IDENTIFIER ON 
SET TEXTSIZE 2147483647 
go 
EXEC GetCustomersWithLowInvoice 
go 

ответ

1

по умолчанию isolation level является read committed. Грязные чтения не могут возникать под этим уровнем изоляции.

Возможно, есть еще одна причина, которую вы упустили.

+0

Не требуется ли вторая процедура в рамках транзакции? –

+0

@ Майкл: спасибо за идею. Никакой транзакции по этому поводу не читается. Интересно, будет ли добавление того, что чтение в транзакции будет иметь какой-либо эффект. –

0

В верхней части процедуры (или непосредственно перед BEGIN TRAN) поставьте следующее.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

Вы также можете выбрать REPEATABLE READ или SERIALIZABLE. Тем не менее, я согласен с Andomar в том, что, вероятно, есть еще одна причина того, что выглядит как изменение уровня изоляции, учитывая, что уровень по умолчанию - READ COMMITTED.

3

Вы не можете предотвратить грязные чтения. Писатели берут эксклюзивные блокировки для предотвращения честности, чтения, чтения. Но есть ничего вы можете сделать, чтобы предотвратить грязные чтения. Грязный читатель должен прекратить делать грязные чтения, период.

Предполагая, что код, который опросает таблицу Customer, находится под вашим контролем, решение заключается в удалении грязного намека на чтение из запроса. Это, вероятно, вызовет конфликт, так как теперь опрос будет заблокирован для записей. Лучшее решение для этого является обеспечение row versioning:

ALTER DATABASE [<DBNAME>] SET ALLOW_SNAPSHOT_ISOLATION ON; 
ALTER DATABASE [<DBNAME>] SET READ_COMMITTED_SNAPSHOT ON; 

Тогда просто опрос от клиента как обычный запрос, без каких-либо намеков. Ваш опрос не будет блокировать записи, поскольку строка-версия приведет к удалению и перенаправлению сканирования запроса в предварительно обновленную, неблокированную версию строки.

Еще одно примечание: опрос каждые 500 мс? Возможно, вы должны использовать механизм уведомления о запросах, чтобы аннулировать ваши кеши, см. The Mysterious Notification.

+0

Спасибо Ремусу. Смешная ситуация здесь с опросом. Эти procs вызывается Java-приложением через JDBC-соединение, поэтому, как утверждается в статье, это аннулирует это решение. Благодарим за ваше предложение. Вероятно, лучшим сценарием будет выполнение опроса на основе пакетного или установочного решения каждые 10 секунд. –

+0

+1 Хорошее чтение между строками, я не понял вопроса о том, что запрос опроса работает с 'nolock' – Andomar

+0

@Andomar: Я не уверен, что правильно прочитал. Возможно, это означает, что «недостающие» части процедуры OP выполняют грязное чтение. Но в качестве общего ответа предотвращение грязных чтений невозможно (за исключением SCH_M_LCK-таблицы, но это DDL), поэтому я дал общий ответ :) –

Смежные вопросы