Блокировка таблиц не позволяет другим пользователям БД влиять на заблокированные вами строки / таблицы. Но блокировки сами по себе НЕ гарантируют, что ваша логика будет работать в согласованном состоянии.
Подумайте о банковской системе. Когда вы оплачиваете счет онлайн, транзакция затрагивает как минимум два аккаунта: ваш аккаунт, с которого снимаются деньги. И счет получателя, на который переводятся деньги. И банковский счет, на который они с радостью переведут все комиссии за обслуживание транзакции. Учитывая (как сейчас все знают), что банки чрезвычайно глупы, допустим, их система работает следующим образом:
$balance = "GET BALANCE FROM your ACCOUNT";
if ($balance < $amount_being_paid) {
charge_huge_overdraft_fees();
}
$balance = $balance - $amount_being paid;
UPDATE your ACCOUNT SET BALANCE = $balance;
$balance = "GET BALANCE FROM receiver ACCOUNT"
charge_insane_transaction_fee();
$balance = $balance + $amount_being_paid
UPDATE receiver ACCOUNT SET BALANCE = $balance
Теперь, без блокировок и транзакций, эта система уязвима к различным условиям гонки, крупнейшим из которых является одновременное выполнение нескольких платежей на вашей учетной записи или учетной записи получателя. Пока ваш код получает ваш баланс и выполняет huge_overdraft_fees () и еще много чего, вполне возможно, что какой-то другой платеж будет запускать тот же тип кода параллельно. Они получат ваш баланс (скажем, 100 долларов), проведут свои транзакции (вынут 20 долларов, которые вы платите, и 30 долларов, с которыми они вас обманывают), и теперь оба пути кода имеют два разных баланса: 80 долларов и 70 долларов. В зависимости от того, какой из них завершится последним, у вас будет один из этих двух остатков на вашем счете вместо 50 долларов, которые должны были у вас остаться (100 - 20 - 30 долларов). В этом случае «ошибка банка в вашу пользу»
Теперь предположим, что вы используете блокировки. Ваш платеж по счету (20 долларов) попадает в трубу первым, поэтому он выигрывает и блокирует вашу учетную запись. Теперь у вас есть эксклюзивное использование, и вы можете вычесть 20 долларов из баланса и спокойно записать новый баланс ... и на вашем счете останется 80 долларов, как и ожидалось. Но ... эээ ... Вы пытаетесь обновить учетную запись получателя, а она заблокирована и заблокирована дольше, чем позволяет код, тайм-аут вашей транзакции ... Мы имеем дело с глупыми банками, поэтому вместо правильной ошибки обработки, код просто вытаскивает exit()
, и ваши 20 долларов растворяются в потоке электронов. Теперь у вас вышло 20 долларов, и вы все еще должны получателю, и ваш телефон был возвращен во владение.
Итак ... вводите транзакции. Вы начинаете транзакцию, вы списываете со своего счета 20 долларов, вы пытаетесь кредитовать получателя на 20 долларов ... и снова что-то взрывается. Но на этот раз вместо exit()
кода может просто работать rollback
, и ваши 20 долларов волшебным образом добавляются обратно в ваш аккаунт.
В итоге все сводится к следующему:
Блокировки удерживают кого-либо от вмешательства в любые записи базы данных, с которыми вы имеете дело. Транзакции не позволяют «более поздним» ошибкам мешать «более ранним» делам, которые вы сделали. Ни одно из них само по себе не может гарантировать, что в конце концов все получится. Но вместе они это делают.
в завтрашнем уроке: «Радость тупиков».
Как вы сказали, вам нужна транзакция
SELECT ... FOR UPDATE
илиSELECT ... LOCK IN SHARE MODE
внутри транзакции, поскольку обычно операции SELECT, независимо от того, находятся ли они в транзакции или нет, не блокируют таблицу. Какой из них вы выберете, будет зависеть от того, хотите ли вы, чтобы другие транзакции могли читать эту строку, пока ваша транзакция выполняется.http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
START TRANSACTION WITH CONSISTENT SNAPSHOT
не поможет вам, поскольку другие транзакции все еще могут появиться и изменить эту строку. Об этом упоминается прямо вверху по ссылке ниже.http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html
источник
Концепции транзакций и блокировки разные. Однако транзакция использовала блокировки, чтобы помочь ей следовать принципам ACID. Если вы хотите, чтобы таблица не позволяла другим читать / писать в тот же момент, когда вы читаете / записываете, вам нужна блокировка для этого. Если вы хотите убедиться в целостности и согласованности данных, вам лучше использовать транзакции. Я считаю смешанные концепции уровней изоляции в транзакциях с блокировками. Пожалуйста, найдите уровни изоляции транзакций, SERIALIZE должен быть желаемым уровнем.
источник
У меня была аналогичная проблема при попытке,
IF NOT EXISTS ...
а затем выполнении,INSERT
которая вызвала состояние гонки, когда несколько потоков обновляли одну и ту же таблицу.Я нашел решение проблемы здесь: Как писать запросы INSERT IF NOT EXISTS в стандартном SQL
Я понимаю, что это не дает прямого ответа на ваш вопрос, но тот же принцип выполнения проверки и вставки как одного оператора очень полезен; вы должны иметь возможность изменить его, чтобы выполнить обновление.
источник
Вы запутались с блокировкой и транзакцией. В RMDB это две разные вещи. Блокировка предотвращает одновременные операции, в то время как транзакция фокусируется на изоляции данных. Прочтите эту замечательную статью, чтобы получить разъяснения и некоторые изящные решения.
источник
Я бы использовал
для начала, а
чтобы закончить.
Все, что вы делаете между ними, изолировано от других пользователей вашей базы данных, если ваш механизм хранения поддерживает транзакции (то есть InnoDB).
источник