Блокировка строк базы данных

#database #concurrency

#База данных #параллелизм

Вопрос:

Моя программа на C # выполняет следующее (псевдокод):

 START TRANSACTION READ COMMITTED
Select isOriginal, * from myTable where tnr = x;
//Record found?
Yes:
  //isOriginal? 
  Yes:
    update myTable set is_active = 0 where tnr = x;
  No:
    delete from myTable where tnr = x;
  //Do some simple logic on the values
  Insert into myTable (newvalues)
No:
  return record_not_found;
END TRANSACTION
  

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

Что должно произойти, так это то, что первая транзакция находит запись и вставляет новую строку, в то время как вторая транзакция возвращает запись, которая не найдена.

Как я могу это исправить? Перевести мою транзакцию в сериализуемый? Проверьте возвращаемое значение update / delete? Каков наилучший способ?

Редактировать:

Это должно работать на Sybase, Oracle и SQL Server.

Комментарии:

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

2. Просто в качестве рекомендации и хорошей практики программирования, никогда SELECT * . Перечислите все нужные вам имена столбцов, даже если это целая таблица. Вы никогда не знаете, когда эта таблица будет изменена другим разработчиком.

3. 3 различные версии БД: sybase, oracle и sql server. Выберите * просто для того, чтобы показать идею, я запрашиваю поля.

Ответ №1:

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

если у каждого параллельного потока есть pid или идентификатор потока или, по крайней мере, уникальная временная метка, просто выполните

 update myTable set lock = <pid> where pid = null limit 1;

select isOriginal, * from myTable where lock = <pid>
  

Ответ №2:

Если вы используете MS SQL, вам нужно будет посмотреть на NOLOCK и ROWLOCK
NOLOCK сообщает SQL Server игнорировать любые типы блокировок и читать непосредственно из реальных таблиц. Плюсом является отличная производительность, минусом — таким образом вы обходите заблокированную систему. ROWLOCK, с другой стороны, запрашивает SQL Server использовать блокировки на уровне строк. Производительность действительно снижается из-за rowlock, поэтому вам нужно определить, нужно ли вам блокировать UPDATES / DELETES

В вашем случае SELECT isOriginal, * FROM myTable WITH (NOLOCK) WHERE tnr=x

Затем UPDATE myTable WITH (ROWLOCK) SET is_active=0 WHERE tnr=x