4

したがって、データベース ID に UUID を使用する必要があるコードがいくつかあります。簡単にするために v4 (ランダム) を使用しましたが、他のよりランダムでないバージョンの UUID を使用する本当の理由はわかりません。私のUUIDクラスは、おおよそ次のように定義されています(簡略化):

class uuid {
public:
    static uuid create_v4();
public:
    // cut out for simplification...
public:
    uint8_t bytes[16];
};

実際の生成コードは次のようになります。

namespace {

uint32_t rand32() {
    // we need to do this, because there is no
    // gaurantee that RAND_MAX is >= 0xffffffff
    // in fact, it is LIKELY to be 0x7fffffff
    const uint32_t r1 = rand() & 0x0ff;
    const uint32_t r2 = rand() & 0xfff;
    const uint32_t r3 = rand() & 0xfff;
    return (r3 << 20) | (r2 << 8) | r1;

}

}

uuid uuid::create_v4() {

    static const uint16_t c[] = {
        0x8000,
        0x9000,
        0xa000,
        0xb000,
    };

    uuid uuid;

    const uint32_t rand_1 = (rand32() & 0xffffffff);
    const uint32_t rand_2 = (rand32() & 0xffff0fff) | 0x4000;
    const uint32_t rand_3 = (rand32() & 0xffff0fff) | c[rand() & 0x03];
    const uint32_t rand_4 = (rand32() & 0xffffffff);

    uuid.bytes[0x00] = (rand_1 >> 24) & 0xff;
    uuid.bytes[0x01] = (rand_1 >> 16) & 0xff;
    uuid.bytes[0x02] = (rand_1 >> 8 ) & 0xff;
    uuid.bytes[0x03] = (rand_1      ) & 0xff;

    uuid.bytes[0x04] = (rand_2 >> 24) & 0xff;
    uuid.bytes[0x05] = (rand_2 >> 16) & 0xff;
    uuid.bytes[0x06] = (rand_2 >> 8 ) & 0xff;
    uuid.bytes[0x07] = (rand_2      ) & 0xff;

    uuid.bytes[0x08] = (rand_3 >> 24) & 0xff;
    uuid.bytes[0x09] = (rand_3 >> 16) & 0xff;
    uuid.bytes[0x0a] = (rand_3 >> 8 ) & 0xff;
    uuid.bytes[0x0b] = (rand_3      ) & 0xff;

    uuid.bytes[0x0c] = (rand_4 >> 24) & 0xff;
    uuid.bytes[0x0d] = (rand_4 >> 16) & 0xff;
    uuid.bytes[0x0e] = (rand_4 >> 8 ) & 0xff;
    uuid.bytes[0x0f] = (rand_4      ) & 0xff;

    return uuid;
}

これは正しいように見えますが、最近、挿入しようとした UUID が重複しているというエラーを DB から受け取りました。これは非常にありそうもないことなので、コードに問題がある可能性があると想定する必要があります。それで、誰かが何か間違っていると思いますか?私のランダムな UUID 生成は十分にランダムではありませんか?

注:ブーストの乱数生成または UUID ライブラリは使用できません。できればいいのですが、特定のバージョンのライブラリがインストールされた特定のシステムに縛られており、これらの機能を搭載するのに十分な新しいバージョンのboostを入手することはほとんど不可能です.

4

1 に答える 1

3

コードは私には合理的であるように思われます。コメントで述べたように、rand()がこのタスクに適しているかどうかについては疑問がありますが、新しいバージョンのライブラリが使用されていると仮定すると、rand()を使用して32ビットのデータを生成するのが妥当な方法のようです。下位ビットが上位ビットと同じくらいランダムであることを保証するために使用されます(コメントにも記載されています)。

rand()関数が適度に良い仕事をしている限り、重複する可能性は非常に低いようです。だから私の推測では、別の種類の失敗があったと思います。頭に浮かぶいくつかの可能性:

  1. time(0)の失敗。これは非常にありそうもないようです。2つの異なる実行でエラーを示すために-1が返された場合は、問題が発生する可能性があります。ただし、失敗する可能性があると思われる唯一の方法は、無効なアドレスが指定された場合です(これは、ここでは絶対に当てはまりません)。
  2. マルチスレッドの使用。rand()はスレッドセーフではないと思います。このコードがマルチスレッドの状況で使用された場合、予期しない動作が発生する可能性があります。
  3. cronが問題を引き起こしています。ワークステーションの時計が正確でなく、サーバーと同期するように自動的に(たとえば、rdateを介して)設定されている場合、特定の時間にcronジョブが繰り返される可能性があります。現在の日付を毎分ファイルにダンプするcronジョブを作成し、日付を繰り返し設定するだけで、この動作を模倣することができました...ファイルに同じ日付/時刻(秒単位)を書き込むことになりました。一度より。時間関数の1秒の解像度では、これによりシードが重複する可能性があります。
  4. UUIDをデータベースに書き込むコードが正しくありません。UUIDジェネレーターが完全に機能している場合でも、同じUUIDをデータベースに2回書き込む別のバグが存在する可能性があります。

ただの野蛮な推測。これらのうち、3つ目は私のお気に入りですが、4つ目は、自分のコードを確認する場合に最初に疑うものです。

于 2012-09-10T23:54:24.017 に答える