Я обнаружил странную проблему с блокировками таблиц MySQL с помощью движка MyISAM.
Допустим, у меня есть такой код:
LOCK TABLES t1 WRITE;
SELECT SQL_NO_CACHE val1 FROM t1 WHERE something; // val1 = old
// some conditions on val1 and logic
UPDATE t1 SET val1 = new WHERE something;
UNLOCK TABLES;
Насколько я знаю, это должно предотвратить любые одновременные обновления. Но это не так. Иногда он просто игнорирует блокировку и «старый» читается в val1 после того, как другой поток изменил его на «новый». Я даже использую SQL_NO_CACHE, чтобы предотвратить получение старых данных по ошибке.
Почему это? Как я могу предотвратить обновление гонки?
Спасибо.
MySQL 5.5.28, MyISAM, PHP 5.2, расширение mysql_ (устарело, но в устаревшем проекте)
Edit:
ОК, это не должно происходить в чистом SQL, поэтому есть код PHP:
<?php
$conn = mysql_connect(...);
...
mysql_query("LOCK TABLES t1, t2 WRITE"); //t2 also used
$result = mysql_fetch_array(mysql_query("SELECT last_user, ... FROM t1 WHERE id = XXX"));
if($result["last_user"] != $session_user) { //is last activity from another than current user?
DoStuffWithUser(...); //custom function which uses t2 table
mysql_query("UPDATE t1 SET last_user = ".$session_user." WHERE id = XXX");
}
mysql_query("UNLOCK TABLES");
...
?>
В результате DoStuffWithUser () вызывается более одного раза для текущего пользователя.
Нет специальных приложений, драйверов, фреймворков. Просто встроенные функции PHP.
Похоже, что проблема в основном (я не могу быть уверен, если исключительно), когда ОДИН пользователь выполняет действие несколько раз - двойной щелчок, некоторый сбой сети, что угодно.