UUID(Universally Unique IDentifier),通用唯一标识符,也叫GUID(Globally Unique IDentifier)。UUID是使用128bits来表示,即16个字节。通常情况下,16个字节来表示UUID所占用的字节也不算多。但是,在某些场景或应用下,由于某些限制,需要UUID所占空间进一步压缩,压缩到8字节,另外,生成的UUID还需要保持有序性。
snowflake原理
轰隆隆~,2010年Twitter提出了snowflake。snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。但是使用scala实现。
其核心思想是:8字节,最高位符号位总是0,接着使用41bit作为毫秒数,10bit作为机器的ID,12bit作为毫秒内的序列号。那么,在c语言中,使用8字节的基本数据类型用来存储id即可。
- 1位标识,最高位符号位总是0,所以生成的ID总是正数。
- 41位时间截(毫秒级),41位的时间截,可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
- 10位的机器ID,显然可以表示1024台不同机器。当然还可以进一步细分,可根据自身需求定义。
- 12位序列号,12位的序列号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号。
理论上,每毫秒可以产生4M个ID。
了解了原理,接下来就实战,干!
snowflake实现
snowflake.h
typedef struct _snowflake_s
{
uint64_t timestamp;
uint16_t machine_id;
uint16_t seq_id;
}snowflake_t;
typedef struct _id_gen_s
{
uint64_t epoch;
snowflake_t gid;
}id_gen_t;
id_gen_t* gid_gen_init(uint16_t machine_id);
void gid_gen_uninit(id_gen_t* handle);
uint64_t gid_gen_get(id_gen_t* handle);
snowflake.c
id_gen_t* gid_gen_init(uint16_t machine_id)
{
id_gen_t* handle = (id_gen_t*)malloc(sizeof(id_gen_t));
handle->epoch = get_time_now_ms();
handle->gid.machine_id = machine_id;
handle->gid.timestamp = get_time_now_ms();
handle->gid.seq_id = 0;
return handle;
}
void gid_gen_uninit(id_gen_t* handle)
{
free(handle);
}
uint64_t gid_gen_get(id_gen_t* handle)
{
uint64_t now = get_time_now_ms();
uint64_t gid = 0;
if (handle->gid.timestamp > now)
{
perror("clock moved backwards, refusing to generate id");
exit(-1);
}
else if (handle->gid.timestamp == now)
{
__sync_fetch_and_add(&handle->gid.seq_id,1);
if (0 == (handle->gid.seq_id & SEQ_ID_MASK))//overflow
now = wait_next_ms(handle->gid.timestamp);//wait next ms
}
else
{
handle->gid.seq_id = 0;
}
handle->gid.timestamp = now;
gid |= (now&TIMESTAMP_MASK) << (MACHINE_ID_BITS+SEQ_ID_BITS);
gid |= (handle->gid.machine_id&MACHINE_ID_MASK) << SEQ_ID_BITS;
gid |= handle->gid.seq_id&SEQ_ID_MASK;
return gid;
}