Блокировка в PHP для получения критического раздела - неожиданные результаты с MySQL

Цель состоит в том, чтобы иметь скрипт PHP с определенным разделом кода, который может выполняться только одним потоком / процессом за один раз.

Некоторые ограничения:

  • семафоры недоступны в моя система
  • В руководстве упоминается, что на многопоточные серверы нельзя полагаться на flock (), поэтому flock () отсутствует. (подтвердил это)

Так думал, что было бы возможно (почему нет?) использовать MySQL для синхронизации, но я получаю неожиданные результаты.

Для тестирования у меня есть две вкладки браузера, открытые с помощью скрипта test.php

mysql_connect ($ dbhost, $ dbusername, $ dbpassword); mysql_select_db ($ database_name);

$ sql = "SELECT my_val FROM my_table WHERE my_var = 'status'"; $ result = mysql_query ($ sql) или die (mysql_error ()); $ row = mysql_fetch_array ($ result, MYSQL_ASSOC); echo "status is:". $ row ['my_val']. "
\ n"; mysql_free_result ($ result);

if ($ row ['my_val'] == 'RUNNING') die ('процесс уже запущен!');

// вход в критическую секцию

$ sql = " ОБНОВЛЕНИЕ my_table SET my_val = 'RUNNING' WHERE my_var = 'status' "; mysql_query ($ sql);

sleep (10); // сделать здесь работу echo 'готовая работа
';

// покинуть критическую секцию. пусть другие знают $ sql = "ОБНОВЛЕНИЕ my_table SET my_val = 'NOT_RUNNING' ГДЕ my_var = 'status'"; mysql_query ($ sql);

Таблица:

CREATE TABLE `my_table` (
  `my_var` varchar(255) NOT NULL default '',
  `my_val` varchar(255) NOT NULL default '',
  PRIMARY KEY  (`my_var`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `my_table`
VALUES (
'status', 'NOT_RUNNING'
) 

Я перехожу на первую вкладку браузера и получаю доступ к сценарию. Я жду 5 секунд, перехожу на другую вкладку и получаю доступ к сценарию, чтобы оба сценария выполнялись параллельно. Я ожидаю, что первая вкладка будет ждать, а вторая вкладка должна выйти после 1-го запроса. Однако обе вкладки ожидают в строке sleep (). Это означает, что первый запрос всегда возвращает NOT_RUNNING.

Некоторые странные вещи:

  • Когда я повторяю вышеописанный эксперимент, я запускаю обе вкладки в FireFox, а затем в третьем другом типе браузера, скажем Chrome, затем работает! (состояние устанавливается на RUNNING во время сна, сценарий завершается рано, когда состояние RUNNING).
  • Когда я повторяю приведенный выше эксперимент с использованием двух разных окон командной строки и запускаю сценарий из командной строки, он работает!
  • I проверьте phpMyAdmin, пока он ожидает, статус обновляется правильно.

Я перепробовал все, заблокировав таблицу, транзакции, SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED, SET autocommit = 1; но всегда один и тот же результат.

Кто-нибудь знает, что здесь происходит? Есть ли хорошее решение для этой проблемы?

Системы проверены: - Win, MySQL 5.0, php 5.3 - Linux, MySQL 5.0.91-community-log, php 5.2.12

Я застрял здесь, спасибо, что заглянули!


ОБНОВЛЕНИЕ:

Спасибо за отправку ответов - я все еще не могу решить Эта проблема. Вот код с GET_LOCK () и session_write_close (), как было предложено: Я также пробовал блокировку на уровне строк, транзакции и разные уровни изоляции. Возможно, это невозможно сделать?


session_write_close();

mysql_connect($dbhost, $dbusername, $dbpassword);
mysql_select_db($database_name);

$sql = "CREATE TABLE IF NOT EXISTS `my_table`  (
  `my_var` varchar(255) NOT NULL default '',
  `my_val` varchar(255) NOT NULL default '',
  PRIMARY KEY  (`my_var`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
";
mysql_query($sql)or die(mysql_error());

mysql_query ("SELECT get_lock('my_lock', 100)");

$sql = "SELECT my_val FROM `my_table` WHERE `my_var`='status' ";
$result = mysql_unbuffered_query($sql)or die(mysql_error());
$row = mysql_fetch_array($result, MYSQL_ASSOC);
echo "status is:".$row['my_val']."
\n"; mysql_free_result($result); if ($row['my_val']=='RUNNING') die ('process is already running!'); // entering critical section $sql = "UPDATE `my_table` SET `my_val`='RUNNING' WHERE `my_var`='status' "; mysql_query ($sql); sleep(10); // do some work here. echo 'finished work
'; // leave critical section. let the others know //$sql = "UPDATE `my_table` SET `my_val`='NOT_RUNNING' WHERE `my_var`='status' "; $sql = "REPLACE INTO `my_table` (`my_var`,`my_val`) VALUES ('status', 'NOT_RUNNING')"; mysql_query ($sql); $result = mysql_query($sql)or die(mysql_error()); mysql_query ("SELECT release_lock('my_lock')"); die();
1
задан Allen Hamilton 25 August 2010 в 00:43
поделиться

2 ответа

Второе окно браузера не зависает при выполнении команды sleep . Он блокируется, потому что сеансы блокируются (и, следовательно, он пытается открыть тот же сеанс). Вы можете закрыть текущий сеанс, если он вам больше не нужен (и, следовательно, не хотите, чтобы он блокировался) с помощью session_write_close ...

2
ответ дан 2 September 2019 в 21:54
поделиться

В MySQL есть функция GET_LOCK .

0
ответ дан 2 September 2019 в 21:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: