2

そのため、別のスクリプトやアプリケーションからの要求を受け入れて処理するためのスクリプトがあります。ただし、スクリプトで実行する必要があるタスクの 1 つは、各要求に一意の連続した「ID」をそれぞれに割り当てることです。

たとえば、アプリケーション A がスクリプトに 1000 のリクエストを送信し、同時にアプリケーション B がスクリプトに 500 のリクエストを送信しているとします。それぞれに 2001 ~ 3500 のように、1500 の固有の連番を付与する必要があります。

ただし、それらの間の順序は重要ではないため、次のように番号を付けることができます。

#2001 for 1st request from A (henceforth, A1)
#2002 for A2
#2003 for B1
#2004 for A3
#2005 for B2
...and so on...

その番号を保存するファイルと、次のような関数を使用して別のロックファイルを作成しようとしました:

private function get_last_id()
{
    // Check if lock file exists...
    while (file_exists("LAST_ID_LOCKED")) {
        // Wait a little bit before checking again
        usleep(1000);
    }

    // Create the lock file
    touch("LAST_ID_LOCKED");

    // Create the ID file for the first time if required
    if (!file_exists("LAST_ID_INDICATOR")) {
        file_put_contents("LAST_ID_INDICATOR", 0);
    }

    // Get the last ID
    $last_id = file_get_contents("LAST_ID_INDICATOR");
    // Update the last ID
    file_put_contents("LAST_ID_INDICATOR", $last_id + 1);

    // Delete the lock file
    unlink("LAST_ID_LOCKED");

    return $last_id;
}

ただし、このコードでは競合状態が発生し、1500 のリクエストを送信すると、最後の ID にかなりの数の欠落が発生します (たとえば、3500 ではなく 3211 にしか到達しません)。

私もこのように群れを使ってみましたが、役に立ちませんでした:

private function get_last_id()
{
    $f = fopen("LAST_ID_INDICATOR", "rw");

    while (true) {
        if (flock($f, LOCK_SH)) {
            $last_id = fread($f, 8192);
            flock($f, LOCK_UN);
            fclose($f);
            break;
        }
        usleep($this->config["waiting_time"]);
    }

    $f = fopen("LAST_ID_INDICATOR", "rw");

    while (true) {
        if (flock($f, LOCK_SH)) {
            $last_id = fread($f, 8192);
            $last_id++;
            ftruncate($f, 0);
            fwrite($f, $last_id);
            flock($f, LOCK_UN);
            fclose($f);
            break;
        }
        usleep($this->config["waiting_time"]);
    }

    return $last_id;
}

では、この状況の解決策を探すために他に何ができるでしょうか?

注:サーバーの制限により、セマフォなどのない PHP 5.2 に制限されています。

4

2 に答える 2

0

ロック機能のあるデータベースにアクセスできる場合は、それを使用できます。たとえば、スケルトン PHP コードを使用する MySQL の場合:

  1. 1 行 1 列のテーブルを作成します (既存のテーブルを「二重使用」したくない場合)。

    $sql = 'CREATE TABLE TABLENAME(COLUMNNAME INTEGER) ENGINE=MyISAM';

    executeSql($sql) ...

  2. カウンター/ID 値を (再) 設定する PHP 関数を作成します。

    $sql = 'UPDATE TABLENAMESET COLUMNNAME=0';

    executeSql($sql); ...

  3. 一意の連続した ID を取得する PHP 関数を作成します。

    $sql = "SELECT GET_LOCK('numberLock',10)";

    executeSql($sql); ...

    $sql = 'SELECT * FROM TABLENAME';

    if ($result = mysqli_query($link, $sql)) {

        $row = mysqli_fetch_row($result);
    
        $wantedId = $row[0];
    
        // do something with the id ...
    
        mysqli_free_result($result);
    

    }

    $sql = 'UPDATE TABLENAMESET COLUMNNAME= COLUMNNAME+1';

    executeSql($sql); ...

    $sql = "SELECT RELEASE_LOCK('numberLock')";

    executeSql($sql); ...

于 2013-10-21T15:15:29.823 に答える