2012-04-27 1 views
2

Я уверен, что у этого есть простое решение, но я пока не смог его найти. При условии, баз данных InnoDB MySQL с уровнем изоляции, установленным в SERIALIZABLE и дали следующую операцию:Во время транзакции, как можно прочитать прочитанную строку до тех пор, пока транзакция не будет выполнена?

BEGIN WORK; 
SELECT * FROM users WHERE userID=1; 
UPDATE users SET credits=100 WHERE userID=1; 
COMMIT; 

Я хотел бы, чтобы убедиться, что как только выбор внутри транзакции выдаются, строка, соответствующий идентификатор пользователя = 1 заблокирован для читает, пока транзакция не будет выполнена. В настоящий момент UPDATE в этой строке будут ждать завершения транзакции, если она находится в процессе, но SELECT просто прочитает предыдущее значение. Я понимаю, что это ожидаемое поведение в этом случае, но мне интересно, есть ли способ заблокировать строку таким образом, чтобы SELECTs также дождались завершения транзакции для возврата значений?

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

Обратите внимание, что я не хочу блокировать всю таблицу для чтения, только определенную строку.

Кроме того, я мог бы добавить логическое «заблокированное» поле в таблицы и установить его в 1 каждый раз, когда начинаю транзакцию, но я действительно не чувствую, что это самое элегантное решение здесь, если нет абсолютно нет другого способа справиться с этим через mysql напрямую.

+0

Вы можете поместить снимок таблицы во временную таблицу: 'CREATE TEMPORARY TABLE t_users SELECT * FROM users;'. – eggyal

+0

Это не помогло бы, я хочу убедиться, что при выполнении транзакции чтение определенной строки будет отклонено, создание временной таблицы поможет только в том случае, если я должен удалить строку из основной таблицы и снова вставить ее обратно после того, как я закончил, но это звучит как слишком много поездок в БД. – Mahn

+0

Плохо, я неправильно понял ваш вопрос. – eggyal

ответ

2

Я нашел обходной путь, а именно:

SELECT ... LOCK IN SHARE MODE устанавливает разделяемую блокировку режима на строках чтения. Блокировка общего режима позволяет другим сеансам читать строки, но не изменять их. Считываемые строки являются последними доступными, поэтому, если они принадлежат к другой транзакции, которая еще не была совершена, чтение блоков до окончания этой транзакции.

(Source)

кажется, что можно просто включить LOCK IN SHARE MODE в критическом ЗЕЬЕСТЕ, которые полагаются на транзакционных данных, и они будут действительно ждать текущих операции, чтобы закончить до извлечения строки/s. Для этого транзакция должна использовать FOR UPDATE явно (в отличие от исходного примера, который я дал). Например., Учитывая следующее:

BEGIN WORK; 
SELECT * FROM users WHERE userID=1 FOR UPDATE; 
UPDATE users SET credits=100 WHERE userID=1; 
COMMIT; 

В любом другом месте в коде я мог бы использовать:

SELECT * FROM users WHERE userID=1 LOCK IN SHARE MODE; 

Поскольку это утверждение не обернута в транзакции, блокировка снимается немедленно, таким образом, не имея влияния на последующее запросов, но если строка, включающая userID = 1, была выбрана для обновления в транзакции, этот оператор будет ждать завершения транзакции, что именно то, что я искал.

2

Вы можете попробовать ВЫБОР ... ДЛЯ ОБНОВЛЕНИЯ блокировка чтения.

ВЫБОР ... ДЛЯ ОБНОВЛЕНИЯ читает последние доступные данные, устанавливая специальные блокировки для каждой строки, которую он читает. Таким образом, он устанавливает те же блокировки, что и искомый SQL UPDATE, установленный в строках.

Пожалуйста, перейдите по следующему сайту: http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html

+0

Уже пробовал, но он только блокирует строку для записи, не читает; плюс я считаю, что не имеет значения, если уровень изоляции уже установлен на SERIALIZE. – Mahn

+0

(Я не могу редактировать предыдущий комментарий по какой-то причине, но я, очевидно, имел в виду SERIALIZABLE) – Mahn

+0

Упрощенный, хотя потому, что он помог найти обходное решение (см. Оригинал сообщения) – Mahn

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