我有一些代码需要使用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 版本以具备这些功能基本上是不可能的。
rand()
进行种子初始化? - Keith Randallrand()
函数并不是一个好主意,特别是由 LCG 生成的数字的低位比较不随机(它们通常具有更短的周期)。你至少应该考虑一些高位,并且如果我是你,我会选择包括时间戳的算法版本,这样你产生重复数字的可能性就会小很多。 - Matteo Italiarand()
采取措施避免 LCG 具有较低的随机性,尽管我可能是错的。 - Evan Terantime()
进行种子初始化的分辨率为 1 秒。你是否可能在同一秒内多次运行种子代码? - Keith Randall