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

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

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

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

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

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

<pre></p> <p>mysql_connect($dbhost, $dbusername, $dbpassword); mysql_select_db($database_name);</p> <p>$sql = "SELECT my_val FROM <code>my_table WHERE my_var = 'status' '; $ result = mysql_query ($ sql) или die (mysql_error ()); $ row = mysql_fetch_array ($результат, 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' ГДЕmy_var = 'status' "; mysql_query ($ sql);

sleep (10); // здесь немного поработать. Echo 'готовая работа
';

//покиньте критическую секцию. пусть остальные знают $ sql = "UPDATE my_table SET my_val = 'NOT_RUNNING' WHERE 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)
  • Когда я повторяю описанный выше эксперимент с использованием двух разных окон командной строки и запускаю сценарий из командной строки, он работает!
  • Я проверяю phpMyAdmin во время ожидания, статус обновляется корректно.

Я перепробовал все, блокировка таблицы, транзакции, УСТАНОВИТЬ УРОВЕНЬ ИЗОЛЯЦИИ ГЛОБАЛЬНОЙ ТРАНЗАКЦИИ ЧИТАТЬ НЕОБЕСПЕЧЕННЫЙ, УСТАНОВИТЬ autocommit = 1;, но всегда всегда один и тот же результат.

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

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

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


ОБНОВЛЕНИЕ:

Спасибо за отправку ваших ответов - я все еще не могу решить эту проблему.Вот код с 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']."<br>\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<br>';

// 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();

Ответы [ 2 ]

2 голосов
/ 24 августа 2010

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

0 голосов
/ 24 августа 2010

Mysql имеет функцию под названием GET_LOCK .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...