#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