SQL Server: предотвращение грязного чтения в хранимой процедуре - PullRequest
2 голосов
/ 27 апреля 2010

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

* 1. Proc, который выполняет 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 со стандартной конфигурацией по умолчанию. Никаких других блокировочных ударов в этих хранимых процедурах не дано.

Эти два хранимых процесса вызываются из клиента 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

Ответы [ 3 ]

3 голосов
/ 27 апреля 2010

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

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

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

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

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

1 голос
/ 27 апреля 2010

По умолчанию уровень изоляции равен read committed. Грязное чтение не может происходить при этом уровне изоляции.

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

0 голосов
/ 27 апреля 2010

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

SET TRANSACTION ISOLATION LEVEL READ COMMITTED 

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...