PHP MySQL get_lock

时间:2011-02-05 18:55:45

标签: php mysql locking

在脚本中,我正在尝试检查是否已使用MySQL GET_LOCK运行相同的脚本。问题是,当脚本试图获得锁定时,它不是空闲的,无论我提供什么参数,它都会永久阻塞。

<?php

class Controller_Refresher extends Controller {
    public function action_run($key) {
        echo date('H:i:s'."\n", time());
        $this->die_if_running();
        $this->run();
        echo date('H:i:s'."\n", time());
    }

    private function die_if_running() {
        $result = DB::query(Database::SELECT, "SELECT IS_FREE_LOCK('refresher_running') AS free")->execute();
        if (! intval($result[0]['free'])) die('Running already');
        $result = DB::query(Database::SELECT, "SELECT GET_LOCK('refresher_running', 1)")->execute();
    }

    private function run() {
        echo "Starting\n";
        ob_flush();
        sleep(10);

        DB::query(Database::SELECT, "SELECT RELEASE_LOCK('refresher_running')")->execute();
    }
}

当我在浏览器的2个标签中运行它时,我得到了例如:

-tab 1-
20:48:16
Starting
20:48:26
-tab 2-
20:48:27
Starting
20:48:37

虽然我想要做的是制作第二个标签die('Running already');

2 个答案:

答案 0 :(得分:2)

注意 - 这个问题可能实际上是由php锁定会话文件引起的:

https://stackoverflow.com/a/5487811/539149

因此,您应该在任何需要并发运行的代码之前调用session_write_close()。我在尝试这个Mutex课程后发现了这个:

http://code.google.com/p/mutex-for-php/

该课程运行良好但我的php脚本仍在逐个运行!

此外,您不需要IS_FREE_LOCK()。只需调用GET_LOCK('refresher_running',0),如果它给你锁定,它将返回1,如果锁定,则返回0。这种方式更具原子性。当然,锁定超时在某些情况下仍然有用,例如当您想要排队任务时,但如果同时请求太多,请注意脚本超时。

扎克莫里斯

答案 1 :(得分:0)

一种选择是依赖文件系统锁而不是数据库。由于这是需要处理的脚本执行,因此无关紧要。来自manual的带有非阻塞独占锁的示例:

$fp = fopen('/tmp/lock.txt', 'r+');

/* Activate the LOCK_NB option on an LOCK_EX operation */
if(!flock($fp, LOCK_EX | LOCK_NB)) {
    die('Running already');
}

/* ... */

fclose($fp);

修改

另一种选择是使用在每次执行开始时创建的状态文件,并在脚本完成后由register_shutdown_function自动删除。

脚本只会检查状态文件的存在,如果它已经存在,执行将停止:

define('statusFile', sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'myjob.running');

//
// If a process is already running then exit
//
if (file_exists(statusFile)) {
   die('Running already');
} else {
   file_put_contents(date('Y-m-d H:i:s'), statusFile);
}

//
// Other code here
//

function shutdown() {
   unlink(statusFile);
}

//
// Remove the status file on completion
//
register_shutdown_function('shutdown');