本发明涉及web应用与开发技术领域,具体涉及一种生成分布式系统中唯一标识的方法。
背景技术:
uuid是通用唯一识别码(universallyuniqueidentifier),在其他语言中也叫guid,可以生成一个长度32位的全局唯一识别码。uuid虽然可以保证全局唯一,但占用32位有些太长,并且是无序的,入库时性能比较差。
分布式系统(distributedsystem)是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。
uuid虽然可以保证全局唯一,如果我们的插入完全无序,不但会导致一些中间节点产生分裂,也会白白创造出很多不饱和的节点,这样大大降低了数据库插入的性能。本发明以时间戳、工作机器id、序列号为基本组成元素,通过算法自动快速生成有序的唯一标识,解决了分布式系统中唯一标识生成的无序性且入库性能差的问题,可广泛应用于web应用与开发过程中。
技术实现要素:
本发明解决的技术问题在于提供一种生成分布式系统中唯一标识的方法;解决了分布式系统中唯一标识生成的无序性且入库性能差的问题。
本发明解决上述技术问题的技术方案是:
所述的方法包含以下几个步骤:
步骤一、确定生成唯一标识组成元素;
步骤二、确定生成变量;
步骤三、编写生成唯一标识构造函数;
步骤四、采用保证线程安全的方式编写获取下一个id的函数;
步骤五、调用生成函数并执行。
所述的自动生成文章摘要的方法,步骤一中,生成唯一标识元素包括时间戳,工作机器id,序列号。时间戳占用41bit,精确到毫秒,总共可以容纳约69年的时间;工作机器id占用10bit,其中高位5bit是数据中心id(datacenterid),低位5bit是工作节点id(workerid),做多可以容纳1024个节点;序列号占用12bit,这个值在同一毫秒同一节点上从0开始不断累加,最多可以累加到4095。
所述的步骤二中,确定生成初始变量,包括初始时间戳、机器id点位数、数据标识id占位数,支持的最大机器id,支持的最大数据标识id,序列在id中占的位数,机器id的偏移量,数据中心id的偏移量,时间截的偏移量,生成序列的掩码,工作节点id,数据中心id,毫秒内序列,上次生成id的时间戳等。
所述的步骤三中,确定并编写生成唯一标识构造函数,构造函数中需判断机器id不能大于已声明的支持的最大机器id,数据中心id不能大于已声明的支持的最大数据标识id。
所述的步骤四中,采用保证线程安全的方式编写获取下一个id的函数。首先判断如果当前时间小于上一次id生成的时间戳,说明系统时钟回退,抛出异常;然后判断如果是同一时间生成的,则进行毫秒内序列增加,如果毫秒内序列已经增长到最大值,则阻塞到下一个毫秒,获得新的时间戳;然后再更新上次生成id的时间戳为当前时间戳;最后移位并通过或运算拼接并组成64位的id。
所述的步骤五中,声明线程池,在线程池中调用步骤四的生成函数并执行id生成。
本发明解决了分布式系统中唯一标识生成的无序性且入库性能差的问题,可广泛应用于web应用和开发过程中。
附图说明
下面结合附图对本发明进一步说明:
图1是本发明方法流程图。
具体实施方式
如图1所示,本发明具体包含以下几个步骤:
步骤一、确定生成唯一标识组成元素;
步骤二、确定生成变量;
步骤三、编写生成唯一标识构造函数;
步骤四、采用保证线程安全的方式编写获取下一个id的函数;
步骤五、调用生成函数并执行。
所述的步骤一中,生成唯一标识元素包括时间戳,工作机器id,序列号。时间戳占用41bit,精确到毫秒,总共可以容纳约69年的时间;工作机器id占用10bit,其中高位5bit是数据中心id(datacenterid),低位5bit是工作节点id(workerid),做多可以容纳1024个节点;序列号占用12bit,这个值在同一毫秒同一节点上从0开始不断累加,最多可以累加到4095。
所述的步骤二中,确定生成初始变量,包括初始时间戳、机器id点位数、数据标识id占位数,支持的最大机器id,支持的最大数据标识id,序列在id中占的位数,机器id的偏移量,数据中心id的偏移量,时间截的偏移量,生成序列的掩码,工作节点id,数据中心id,毫秒内序列,上次生成id的时间戳等。
//初始时间截(2017-01-01)
privatestaticfinallongsd_initial_time_stamp=1483200000000l;
//机器id所占的位数
privatestaticfinallongsd_worker_id_bits=5l;
//数据标识id所占的位数
privatestaticfinallongsd_datacenter_id_bits=5l;
//支持的最大机器id
privatestaticfinallongsd_max_worker_id=~(-1l<<sd_worker_id_bits);
//支持的最大数据标识id
privatestaticfinallongsd_max_datacenter_id=~(-1l<<sd_datacenter_id_bits);
//序列在id中占的位数
privatefinallongsd_sequence_bits=12l;
//机器id的偏移量(12)
privatefinallongsd_workerid_offset=sd_sequence_bits;
//数据中心id的偏移量(12+5)
privatefinallongsd_datacenterid_offset=sd_sequence_bits+sd_sequence_bits;
//时间截的偏移量
privatefinallongsd_timestamp_offset=sd_sequence_bits+sd_worker_id_bits+sd_datacenter_id_bits;
//生成序列的掩码
privatefinallongsd_sequence_mask=~(-1l<<sd_sequence_bits);
//工作节点id(0~31)
privatelongsd_workerid;
//数据中心id(0~31)
privatelongsd_datacenterid;
//毫秒内序列(0~4095)
privatelongsd_sequence=0l;
//上次生成id的时间截
privatelongsd_lasttimestamp=-1l;
所述的步骤三中,确定并编写生成唯一标识构造函数,构造函数中需判断机器id不能大于已声明的支持的最大机器id,数据中心id不能大于已声明的支持的最大数据标识id。
publicsfidgenerator(longsd_workerid,longsd_datacenterid){
if(sd_workerid>sd_max_worker_id||sd_workerid<0){
thrownewillegalargumentexception(string.format("workerid不能大于%d或小于0",sd_max_worker_id));
}
if(sd_datacenterid>sd_max_datacenter_id||sd_datacenterid<0){
thrownewillegalargumentexception(string.format("datacenterid不能大于%d或小于0",sd_max_datacenter_id));
}
this.sd_workerid=sd_workerid;
this.sd_datacenterid=sd_datacenterid;
}
所述的步骤四中,采用保证线程安全的方式编写获取下一个id的函数。首先判断如果当前时间小于上一次id生成的时间戳,说明系统时钟回退,抛出异常;然后判断如果是同一时间生成的,则进行毫秒内序列增加,如果毫秒内序列已经增长到最大值,则阻塞到下一个毫秒,获得新的时间戳;然后再更新上次生成id的时间戳为当前时间戳;最后移位并通过或运算拼接并组成64位的id。
publicsynchronizedlongsd_nextid(){
longtimestamp=system.currenttimemillis();
//如果当前时间小于上一次id生成时间戳,抛出异常
if(timestamp<sd_lasttimestamp){
thrownewruntimeexception("当前时间小于上一次记录的时间戳!");
}
//如果是同一时间生成的,则进行毫秒内序列
if(sd_lasttimestamp==timestamp){
sd_sequence=(sd_sequence+1)&sequence_mask;
//sd_sequence等于0说明毫秒内序列已经增长到最大值
if(sd_sequence==0){
//阻塞到下一个毫秒,获得新的时间戳
timestamp=tilnextmillis(sd_lasttimestamp);
}
}
else{//时间戳改变,毫秒内序列重置
sd_sequence=0l;
}
sd_lasttimestamp=timestamp;//上次生成id的时间戳
//移位并通过或运算拼到一起组成64位的id
return
((timestamp-sd_initial_time_stamp)<<sd_timestamp_offset)
|(sd_datacenterid<<sd_datacenterid_offset)
|(sd_workerid<<sd_workerid_offset)
|sd_sequence;
}
protectedlongsd_tilnextmillis(longlasttimestamp){
longtimestamp=system.currenttimemillis();
while(timestamp<=lasttimestamp){
timestamp=system.currenttimemillis();
}
returntimestamp;
}
所述的步骤五中,声明线程池,在线程池中调用步骤四的生成函数并执行id生成;
sfidgeneratorsd_idgenerator=newsfidgenerator(1,1);
//线程池并行执行10000次id生成
executorservicesd_executorservice=executors.newcachedthreadpool();
for(inti=0;i<10000;i++){
sd_executorservice.execute(newrunnable(){
@override
publicvoidrun(){
longid=sd_idgenerator.nextid();
system.out.println(id);
}
});
}
executorservice.shutdown()。