2011-01-18 6 views
5

Учитывая В следующей таблицеКак настроить блокировку операции чтения-манипулирования-записи?

Key(KeyId int, Sequence varchar(14))

Значение последовательности является пользовательским ключом автоматического приращения совмещая буквы и цифры, в частности, потребность клиента в его систему.

Мы создали функцию GetNextSequence(), которая должна возвращать следующее значение последовательности. Шаг к чтению и обновления последовательности идет как следовать

  1. Считать значение последовательности с помощью KeyId: SELECT Sequence FROM [Key] WHERE KeyId = @Id
  2. Разбирает значение последовательности и определить следующее значение
  3. Записать значение последовательности в таблицу: UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id

Здесь C# код (упрощенный для ясности):

var transaction = connection.BeginTransaction(IsolationLevel.RepeatableRead); 
var currentSequenceValue = SqlUtils.ExecuteScalar(connection, transaction, "SELECT Sequence FROM [Key] WHERE KeyId = @Id", new SqlParameter("@Id", keyId)); 
var updatedSequenceValue = ParseSequence(currentSequenceValue); 
SqlUtils.ExecuteScalar(connection, transaction, "UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id", new SqlParameter("@Id", keyId), new SqlParameter("@Sequence", updatedSequenceValue)); 
transaction.Commit(); 
return updatedSequenceValue; 

Наша задача находиться в том, что два разных сервера могут получить доступ к той же самой последовательности, и мы в конечном итоге получить тупиковый

транзакции (идентификатор процесса X) был заведен в тупик на ресурсах блокировки с другим процессом и был выбран в качестве тупиковой жертвы. Перезапустите транзакцию.

В C#, я попытался установить другую комбинацию блокировки, как изоляция IsolationLevel.RepeatableRead или IsolationLevel.Serializable или в SQL транзакций с использованием таблицы намек ROWLOCK и HOLDLOCK, но без успеха.

Я хочу, чтобы каждый сервер мог читать, манипулировать и обновлять последовательность в атомном режиме. Каков правильный способ установки блокировки для этой ситуации?

+0

на боковой ноте это звучит как эта проблема: http://www.codinghorror.com/blog/2008/08/deadlocked.html – BrokenGlass

ответ

1

Я предлагаю использовать эксклюзивную блокировку на уровне строк в течение всего периода транзакции (ROWLOCK, XLOCK, HOLDLOCK). До сих пор недостаточно использовать подсказки и т. Д.

BEGIN TRAN 
    SELECT Sequence FROM [Key] WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE KeyId = @Id 

    Parse the sequence value and determine the next value 

    UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id 
COMMIT 

Хотя, я бы смотреть на, по крайней мере сокращения масштабов единой операции

UPDATE [Key] WITH (ROWLOCK, XLOCK, HOLDLOCK) 
    SET Sequence = dbo.scalarudf(...) 
    WHERE KeyId = @Id 

Edit: вам не нужно HOLDLOCK, если вы используете SERIALIZABLE. И «RepeatableRead» может быть недостаточным из-за того, что диапазоны заблокированы.

+0

О, я должен указать, что «Parse sequence» выполняется на C#, а не в SQL Server. –

+1

@ Pierre-Alain Vigeant: вам все еще нужны подсказки и транзакции. Можете ли вы переместить C# в fucntion CLR или в SQL? – gbn

+0

Кажется, что он работает с 'ROWLOCK, XLOCK, HOLDLOCK', но мне нужно дальнейшее тестирование, которое я буду делать завтра. –

2

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

Ситуация заключается в том, что процесс A получает блокировку чтения в строке X. Затем процесс B получает блокировку чтения, когда A работает на «стороне клиента» (в рамках серверной программы). Затем A запрашивает обновление до блокировки записи, когда B работает на стороне клиента, после чего ему предлагается дождаться блокировки чтения B. Затем B запрашивает блокировку записи и является tols, чтобы ждать, пока A не выпустит его Чтение. Оба теперь ждут друг друга, чтобы они могли приобрести более эксклюзивный замок Write.

Решение является эксклюзивным замком; вы можете указать это с помощью подсказки XLOCK.Исключительная блокировка - это, в основном, блокировка уровня записи, полученная для чтения, и используется в этом конкретном случае, когда вы ожидаете написать что-то, что вы читаете. Как отмечено в комментариях, исключительная блокировка поддерживается только в том случае, если оператор выполняется в рамках явной транзакции, поэтому убедитесь, что вы устанавливаете его во время «единицы работы», которая считывает значение, определяя, как продвигаться его, а затем обновить.

Я бы использовал это на уровне строк (ROWLOCK), если только вы не обновляете множество одинаковых последовательностей одновременно; приобретение эксклюзивной блокировки на странице или на уровне таблиц делает EVERYBODY ожиданием данных, которые вам не нужны, если вы работаете только по одной строке за транзакцию.

+0

XLOCK сохраняется только для текущего оператора, если только в явной транзакции. Этого само по себе недостаточно. Поэтому, если используется для SELECT, вы можете по-прежнему блокировать UPDATE. – gbn

+0

Я принимал явную транзакцию; функция GetNextSequence() является, начиная с слова go, атомной «единицей работы». – KeithS

+0

Правда, но должно быть более ясно о сохранении эксклюзивной блокировки – gbn

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