ホームページ > バックエンド開発 > PHPチュートリアル > 一意の ID を生成するアルゴリズムに重複はありますか?

一意の ID を生成するアルゴリズムに重複はありますか?

WBOY
リリース: 2016-06-20 12:41:13
オリジナル
1455 人が閲覧しました

インターネット上で一意の ID を生成するコードを見たことがありますが、この PHP のコードは ID を生成するときに非常に繰り返しが多いとコメントしています。

class Idwork{    const debug = 1;    static $workerId;    static $twepoch = 1361775855078;    static $sequence = 0;    const workerIdBits = 4;    static $maxWorkerId = 15;    const sequenceBits = 10;    static $workerIdShift = 10;    static $timestampLeftShift = 14;    static $sequenceMask = 1023;    private static $lastTimestamp = -1;    public function __construct($workId)    {        if ($workId > self::$maxWorkerId || $workId < 0) {            throw new Exception('worker Id can\'t be greater than 15 or less than 0');        }        self::$workerId = $workId;    }    public function timeGen()    {        //获得当前时间戳        $time = explode(' ', microtime());        $time2 = substr($time[0], 2, 3);        $timestramp = $time[1] . $time2;        return $time[1] . $time2;    }    public function tilNextMillis($lastTimestamp)    {        $timestamp = $this->timeGen();        while ($timestamp <= $lastTimestamp) {            $timestamp = $this->timeGen();        }        return $timestamp;    }    public function nextId()    {        $timestamp = $this->timeGen();//1452043798718        if (self::$lastTimestamp == $timestamp) {            self::$sequence = self::$sequence + 1 & self::$sequenceMask;            if (self::$sequence == 0) {                $timestamp = $this->tilNextMillis(self::$lastTimestamp);            }        } else {            self::$sequence = 0;        }        if ($timestamp < self::$lastTimestamp) {            throw new Excwption('Clock moved backwards.  Refusing to generate id for ' . (self::$lastTimestamp - $timestamp) . ' milliseconds');        }        self::$lastTimestamp = $timestamp;        $nextId = sprintf('%.0f', $timestamp) - sprintf('%.0f', self::$twepoch) | self::$workerId << self::$workerIdShift | self::$sequence;        return $nextId;    }}$Idwork = new Idwork(1);$a = $Idwork->nextId();
ログイン後にコピー


そして以下の一意の ID を生成するこの方法
function get_order_sn(){    mt_srand((double) microtime() * 1000000);    return date('Ymd') . str_pad(mt_rand(1, 99999), 4, '0', STR_PAD_LEFT);}echo get_order_sn();
ログイン後にコピー


これら 2 つの方法のどちらが優れていますか、または高い同時実行条件で重複せずに一意の ID を生成する他の方法はありますか


ディスカッションへの返信 (解決策)

PHP を使用してください組み込み関数 uniqid

参考: http://php.net/manual/zh/function.uniqid.php

echo uniqid(), PHP_EOL;echo uniqid(), PHP_EOL;
ログイン後にコピー
ログイン後にコピー
568c83e69c671568c83e69c671
ログイン後にコピー
ログイン後にコピー
function get_order_sn(){    mt_srand((double) microtime() * 1000000);     return date('Ymd') . str_pad(mt_rand(1, 99999), 4, '0', STR_PAD_LEFT);}echo get_order_sn(), PHP_EOL;echo get_order_sn(), PHP_EOL;
ログイン後にコピー
ログイン後にコピー
201601063753201601063753
ログイン後にコピー
ログイン後にコピー
は明らかにできませんテスト自体に合格します テスト

$Idwork = new Idwork(1);echo $Idwork->nextId(), PHP_EOL;echo $Idwork->nextId(), PHP_EOL;$test = new Idwork(1);echo $test->nextId(), PHP_EOL;echo $test->nextId(), PHP_EOL;
ログイン後にコピー
ログイン後にコピー
は独自のテスト
に合格できますが、同時実行性はどうなるのでしょうか?

コードの最初の部分を Idwork.php とすると、

$mch = curl_multi_init();for($i=0; $i<4; $i++) {  $ch = curl_init('http://localhost/Idwork.php');  curl_multi_add_handle($mch, $ch);}$running = NULL;do {    usleep ( 10000 );    curl_multi_exec ( $mch, $running );} while ( $running > 0 );
ログイン後にコピー
8015005880150059801500588015005980150058801500598015005880150059801500588015005980150058801500598015005880150059
ログイン後にコピー
は明らかに get_order_sn メソッドの同時実行テスト


date('YmdHis') に合格できません。秒を取得する、繰り返しの確率は比較的小さい

方法を紹介しますが、桁の問題は調整次第です

長さを制限しない方法もありますの数値を使用できるメソッド(年月日と10秒がわかる+乱数)(26桁)

$order_sn = date('YmdHis').substr(time() ,-5).substr(microtime (),2,5).rand(10,99);


利点:
1. データベースを操作する必要がなく、パフォーマンスが高い。
2. 注文が生成されるおおよその時間を確認するのはそれほど難しくありません。
3. 複数のリクエストを処理できるのはプログラムだけです。 100万分の1秒以内に同時に注文番号を生成すると、同時に発生する10から99までの乱数が同じになり、重複した注文番号が表示されます。

2016010612111453474171874820160106121114534741718783201601061211145347417187832016010612111453474171871920160106121114534741718730201601061211145347417187772016010612111453474171871320160106121114534741718782
ログイン後にコピー

まだ同時実行テストに合格できません

com_create_guid のみがグローバル一意識別子 (GUID) を生成します
重複がないことを確実に保証できます同じサーバー内
複数のサーバー間で重複があるかどうかは、テストしないと確認できません

GUID はグローバルに一意であることがわかっています

com_create_guid のみがグローバルに固有識別子(GUID)
同一サーバー内で重複がないことが確実に保証できる
複数のサーバー間で重複があるかどうかはテストしないと確認できません

そしてGUID世界で唯一の番号として知られています

モデレーターさん、熱心なご回答ありがとうございますが、これを注文番号として使用すると長すぎます。 12桁の純粋な番号を注文番号として使用すると、可能な限り一意性を確保するための注文番号の設定方法


com_create_guid のみがグローバル一意識別子 (GUID) を生成します
確実に存在することを保証できます同一サーバー内での重複はありません
複数のサーバー間で重複があるかどうかはテストしないと確認できません

そして GUID は世界的に一意であると言われています

モデレーターさん、熱心な回答ありがとうございます。これを注文番号として使用すると長すぎます。12 桁の純粋な番号を注文番号として使用する場合、できるだけ一意性を確保するための注文番号の設定方法


+ ユーザー ID + 製品シリアル番号 + 乱数

echo uniqid(), PHP_EOL;echo uniqid(), PHP_EOL;
ログイン後にコピー
ログイン後にコピー
568c83e69c671568c83e69c671
ログイン後にコピー
ログイン後にコピー
function get_order_sn(){    mt_srand((double) microtime() * 1000000);     return date('Ymd') . str_pad(mt_rand(1, 99999), 4, '0', STR_PAD_LEFT);}echo get_order_sn(), PHP_EOL;echo get_order_sn(), PHP_EOL;
ログイン後にコピー
ログイン後にコピー
201601063753201601063753
ログイン後にコピー
ログイン後にコピー
明らかに独自のテストに合格することはできません

$Idwork = new Idwork(1);echo $Idwork->nextId(), PHP_EOL;echo $Idwork->nextId(), PHP_EOL;$test = new Idwork(1);echo $test->nextId(), PHP_EOL;echo $test->nextId(), PHP_EOL;
ログイン後にコピー
ログイン後にコピー
は合格できますそれ自体をテストします
しかし、同時実行性についてはどうでしょうか?



echo uniqid(), PHP_EOL;echo uniqid(), PHP_EOL;function get_order_sn(){    mt_srand((double) microtime() * 1000000);      return date('Ymd') . str_pad(mt_rand(1, 99999), 4, '0', STR_PAD_LEFT);}echo get_order_sn(), PHP_EOL;echo get_order_sn(), PHP_EOL;
ログイン後にコピー

これら 2 つの生成メソッドは単独ではテストできません。どのようにテストされるのでしょうか?



com_create_guid のみがグローバル一意識別子 (GUID) を生成します
同じサーバー内で繰り返されないことを確実に保証できます
複数のサーバー サーバー間で重複があるかどうかはテストしないと確認できません

GUID は世界で唯一と言われています

モデレーターの熱心な回答に感謝しますが、これを順序として使用すると12 桁の場合は長すぎます。注文番号として純粋な番号を使用する場合、できるだけ一意性を確保するための注文番号の設定方法
時間 + ユーザー ID + 製品シリアル番号 + 乱数
この方法は良いですが、生成されるビットを保証することはできません この順序番号はユーザーの ID が大きくなるにつれてどんどん大きくなります




com_create_guid のみがグローバル一意識別子 (GUID) を生成します
同一サーバー内で重複がないことが保証できます
複数のサーバー間で重複があるかどうか

また、GUID は世界で唯一であると言われています

モデレーターさん、熱心なご回答ありがとうございますが、これを注文番号として使用すると長すぎますが、12桁の純粋な番号を注文番号として使用すると、できるだけ一意性を確保するために注文番号を設定する方法を教えてください。可能な限り
時刻 + ユーザー ID + 製品シリアル番号 + 乱数
この方法は良いですが、生成される桁数は保証されません。この注文番号はどんどん大きくなります。ユーザーのIDが増加するにつれて
長さは自分で設定できます。また、数には上限があります。 。 。

1 つの操作で 2 つの ID が生成される場合、少なくとも 2 つの ID が異なることが保証される必要があります。
これが保証できない場合、2 つのリクエストで取得された ID が異なることをどのように確認できますか? ?

Idwork クラスは、1 回のプログラム実行では重複がないことを保証できますが、複数の実行では重複が発生します

com_create_guid は確かに非常に長いですが、このため、一意であることが保証されています

常識的に考えて、これまでに出現したことのない ID を取得したい場合は、それが出現した ID シーケンスに含まれていないことを確認する必要があります
そして、この ID シーケンスは、表示されたデータは公開の場所に保存する必要がありますが、共有の競合を防ぐためにも必要です

したがって、最良の選択はデータベースの自動インクリメント フィールドを使用することです

皆さん、熱心な回答とありがとうございました。たくさんの知識を学びました

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート