Как вернуть ошибку физической блокировки без ожидания в MySQL

в моем проекте с использованием java, ibatic и MySql.

Перед любым обновлением данных в базе данных я блокирую таблицу с помощью SELECT FOR UPDATE запроса

eg: SELECT * FROM MEM_MST WHERE MEM_ID = #memId# FOR UPDATE

Это делает блокировку таблицы правильно. но проблема в том, например, если два клиента обновляют таблицу одновременно. первый блокирует таблицу и обновляет, а второй ожидает обновления до тех пор, пока блокировка не будет снята. После этого второй также обновляет данные. Итак, первые обновленные данные перезаписываются. См. следующее объяснение:

Time |  Client 1           | Client 2  
-------------------------------------------------
1    | SELECT FOR UPDATE   | 
2    | UPDATE              | SELECT FOR UPDATE (Waiting) 
3    | COMMIT              | (Waiting)
4    |                     | UPDATE
5    | (Overwritten)       | COMMIT

Таким образом, обновленные данные Клиента 1 теряются.

Чего я хочу, так это просто вернуть сообщение об ошибке Клиенту 2 вместо того, чтобы ждать, пока будет снята блокировка.

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


P.S:

Я уже установил тайм-аут ожидания блокировки на 0 в переменных запуска, но все еще требуется около 2 секунд, чтобы сообщить сообщение «Превышен тайм-аут ожидания блокировки».

кроме того, SELECT FOR UPDATE NO WAIT не работает в MySQL


person AKZap    schedule 12.05.2012    source источник
comment
Если у вас есть контроль над каждым оператором блокировки, вы можете использовать именованные блокировки, например. IS_USED_LOCK().   -  person eggyal    schedule 12.05.2012
comment
@eggyal спасибо за ваш комментарий. попытка с ‹code›IS_USED_LOCK()‹/code› в порядке. Но на самом деле это не блокировка ни таблицы, ни базы данных. Кроме того, я должен тщательно снимать каждую блокировку с помощью кода RELEASED_LOCK/code после каждого оператора блокировки. Если я пропущу снятие блокировки, она будет заблокирована до серьезного перезапуска.   -  person AKZap    schedule 18.05.2012
comment
На самом деле это не блокирует таблицу, но предоставляет метод синхронизации. Я считаю, что нет собственного способа сделать то, о чем вы просите. Что касается упомянутого вами недостатка, ограничение уже существует с вашим первоначальным подходом: транзакция должна быть завершена, иначе строка будет заблокирована навсегда.   -  person RandomSeed    schedule 19.12.2013


Ответы (1)


К сожалению, innodb_lock_wait_timeout нельзя установить ниже чем 1.

Вам придется реализовать собственный механизм синхронизации. В качестве альтернативы предложению Эггьяла вы можете добавить поле «семафор» в свою критическую таблицу. Основной рабочий процесс (псевдокод):

START TRANSACTION;
SELECT locked FROM MEM_MST WHERE MEM_ID = @id FOR UPDATE;
if (previous_result == 1) {
    ROLLBACK;
    throw new Exception();
}
UPDATE MEM_MST SET locked = 1 WHERE MEM_ID = @id;
COMMIT; -- from now on, concurrent attempts will fail immediately

START TRANSACTION;
UPDATE MEM_MST SET whatever = @some_value WHERE MEM_ID = @id;
UPDATE MEM_MST SET locked = 0 WHERE MEM_ID = @id; -- release lock
COMMIT;
person RandomSeed    schedule 19.12.2013