4

所以我有一些代码需要使用 UUID 作为数据库 ID。为简单起见,我使用了 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 是重复的。由于这应该是极不可能的,我不得不假设我的代码可能存在问题。所以有人看到有什么不对吗?我的随机 UUID 生成是否不够随机?

注意:我不能使用 boost 的随机数生成或它的 UUID 库。我希望可以,但我与安装了特定版本的库的特定系统相关联,并且获得足够新的 boost 版本来拥有这些功能几乎是不可能的。

4

1 回答 1

3

该代码对我来说似乎是合理的。正如评论中所提到的,关于 rand() 是否是这个任务的一个不错的选择存在一些问题,但是假设一个新版本的库正在被更新,你对它的使用似乎是一种产生 32 位数据的合理方法用于确保较低位与较高位一样随机(您在评论中也提到过)。

因此,只要 rand() 函数做得还不错,您似乎不太可能得到重复。所以我的猜测是有另一种失败。想到的一些可能性:

  1. 时间(0)失败。这似乎极不可能。如果它返回 -1 以指示在两次不同的运行中出现错误,那么它可能会导致问题。但是,它应该能够失败的唯一方法是给它一个无效的地址(这里绝对不是这种情况)。
  2. 多线程使用。我不认为 rand() 是线程安全的。如果在多线程情况下使用此代码,则可能会导致意外行为。
  3. Cron 造成了困难。如果工作站上的时钟不准确,并且它被自动设置(例如,通过 rdate)与某个服务器同步,那么它可能会导致 cron 作业在某个时间重复。我能够通过创建一个 cron 作业来模仿这种行为,每分钟将当前日期转储到一个文件中,然后重复设置日期......它最终将相同的日期/时间(到第二个)写入文件更多不止一次。对于时间函数的一秒分辨率,这很容易导致重复种子。
  4. 将 UUID 写入数据库的代码不正确。即使 UUID 生成器工作正常,也可能存在将相同 UUID 两次写入数据库的不同错误。

只是疯狂的猜测。其中,第三个是我最喜欢的,但如果我正在审查自己的代码,我会首先怀疑第四个。

于 2012-09-10T23:54:24.017 回答