sequence介绍 sequence是一个基于雪花算法(Snowflake)实现的64位自增ID算法,实现语言是JAVA。 其在雪花算法的基础上,做了一些优化,解决了原生算法的一些痛点:支持用户自定义允许时间回拨的范围;解决了跨毫秒时起始值从0开始增长的问题;解决了高并发场景中获取时间戳性能的问题; 雪花算法的介绍可以参考博主上篇文章:还在用数据库自增ID做主键?建议了解一下雪花算法生成的分布式ID性能 从官方的性能测试数据来看,sequence每秒可以生成最多418万个有序的ID,即TPS400ws,已经基本满足绝大部分的业务场景。 源代码及使用 核心代码一共两个类,分别是Sequence。java和SystemClock。java。 Sequence是产生分布式ID的核心,SystemClock主要解决了高并发场景下System。currentTimeMills()的性能问题。 大家也可根据自己的需要,修改其中的部署属性值,比如自定义机器标识,起始时间戳等。 Sequence。java:publicclassSequence{privatestaticfinalLoggerlogLoggerFactory。getLogger(Sequence。class);时间起始标记点,作为基准,一般取系统的最近时间一旦确定不能变动,确定后改变此值可能造成id重复privatefinallongtwepoch1519740777809L;5位的机房idprivatefinallongdatacenterIdBits5L;5位的机器idprivatefinallongworkerIdBits5L;每毫秒内产生的id数:2的12次方个privatefinallongsequenceBits12L;protectedfinallongmaxDatacenterId1L(1LdatacenterIdBits);protectedfinallongmaxWorkerId1L(1LworkerIdBits);privatefinallongworkerIdShiftsequenceBits;privatefinallongdatacenterIdShiftsequenceBitsworkerIdBits;privatefinallongtimestampLeftShiftsequenceBitsworkerIdBitsdatacenterIdBits;privatefinallongsequenceMask1L(1LsequenceBits);所属机房idprivatefinallongdatacenterId;所属机器idprivatefinallongworkerId;并发控制序列privatelongsequence0L;上次生产ID时间戳privatelonglastTimestamp1L;获取IPprivatestaticvolatileInetAddressLOCALADDRESSnull;privatestaticfinalPatternIPPATTERNPattern。compile(d{1,3}(。d{1,3}){3,5}34;);默认的无参构造publicSequence(){this。datacenterIdgetDatacenterId();this。workerIdgetMaxWorkerId(datacenterId);}有参构造器,我们可以自定义机器id和机房idpublicSequence(longworkerId,longdatacenterId){if(workerIdmaxWorkerIdworkerId0){thrownewIllegalArgumentException(String。format(WorkerIdcantbegreaterthandorlessthan0,maxWorkerId));}if(datacenterIdmaxDatacenterIddatacenterId0){thrownewIllegalArgumentException(String。format(DatacenterIdcantbegreaterthandorlessthan0,maxDatacenterId));}this。workerIdworkerId;this。datacenterIddatacenterId;}基于网卡MAC地址计算余数作为数据中心,如果不想用网卡可以自定义protectedlonggetDatacenterId(){longid0L;try{NetworkInterfacenetworkNetworkInterface。getByInetAddress(getLocalAddress());if(nullnetwork){id1L;}else{byte〔〕macnetwork。getHardwareAddress();if(null!mac){id((0x000000FF(long)mac〔mac。length2〕)(0x0000FF00(((long)mac〔mac。length1〕)8)))6;idid(maxDatacenterId1);}}}catch(Exceptione){log。warn(getDatacenterId:e。getMessage());}returnid;}基于MACPID的hashcode获取16个低位protectedlonggetMaxWorkerId(longdatacenterId){StringBuildermpIdnewStringBuilder();mpId。append(datacenterId);StringnameManagementFactory。getRuntimeMXBean()。getName();if(name!nullname。length()0){GETjvmPidmpId。append(name。split()〔0〕);}MACPID的hashcode获取16个低位return(mpId。toString()。hashCode()0xffff)(maxWorkerId1);}获取下一个IDpublicsynchronizedlongnextId(){获取当前时间戳,这里通过SystemClock优化获取性能longtimestamptimeGen();时间回拨了if(timestamplastTimestamp){longoffsetlastTimestamptimestamp;if(offset5){try{休眠双倍差值后重新获取,再次校验wait(offset1);timestamptimeGen();if(timestamplastTimestamp){thrownewRuntimeException(String。format(Clockmovedbackwards。Refusingtogenerateidfordmilliseconds,offset));}}catch(Exceptione){thrownewRuntimeException(e);}}else{thrownewRuntimeException(String。format(Clockmovedbackwards。Refusingtogenerateidfordmilliseconds,offset));}}if(lastTimestamptimestamp){相同毫秒内,序列号自增sequence(sequence1)sequenceMask;if(sequence0){同一毫秒的序列数已经达到最大timestamptilNextMillis(lastTimestamp);}}else{不同毫秒内,序列号置为13随机数sequenceThreadLocalRandom。current()。nextLong(1,3);}lastTimestamptimestamp;时间戳部分数据中心部分机器标识部分序列号部分return((timestamptwepoch)timestampLeftShift)(datacenterIddatacenterIdShift)(workerIdworkerIdShift)sequence;}protectedlongtilNextMillis(longlastTimestamp){longtimestamptimeGen();while(timestamplastTimestamp){timestamptimeGen();}returntimestamp;}protectedlongtimeGen(){returnSystemClock。INSTANCE。currentTimeMillis();}FindfirstvalidIPfromlocalnetworkcardreturnfirstvalidlocalIPpublicstaticInetAddressgetLocalAddress(){if(LOCALADDRESS!null){returnLOCALADDRESS;}LOCALADDRESSgetLocalAddress0();returnLOCALADDRESS;}privatestaticInetAddressgetLocalAddress0(){InetAddresslocalAddressnull;try{localAddressInetAddress。getLocalHost();if(isValidAddress(localAddress)){returnlocalAddress;}}catch(Throwablee){log。warn(Failedtoretrievingipaddress,e。getMessage(),e);}try{EnumerationNetworkInterfaceinterfacesNetworkInterface。getNetworkInterfaces();if(interfaces!null){while(interfaces。hasMoreElements()){try{NetworkInterfacenetworkinterfaces。nextElement();EnumerationInetAddressaddressesnetwork。getInetAddresses();while(addresses。hasMoreElements()){try{InetAddressaddressaddresses。nextElement();if(isValidAddress(address)){returnaddress;}}catch(Throwablee){log。warn(Failedtoretrievingipaddress,e。getMessage(),e);}}}catch(Throwablee){log。warn(Failedtoretrievingipaddress,e。getMessage(),e);}}}}catch(Throwablee){log。warn(Failedtoretrievingipaddress,e。getMessage(),e);}log。error(Couldnotgetlocalhostipaddress,willuse127。0。0。1instead。);returnlocalAddress;}privatestaticbooleanisValidAddress(InetAddressaddress){if(addressnulladdress。isLoopbackAddress()){returnfalse;}Stringnameaddress。getHostAddress();return(name!null!0。0。0。0。equals(name)!127。0。0。1。equals(name)IPPATTERN。matcher(name)。matches());}} SystemClock。java:利用ScheduledExecutorService实现高并发场景下System。curentTimeMillis()的性能问题的优化。publicenumSystemClock{INSTANCE(1);privatefinallongperiod;privatefinalAtomicLongnowTime;privatebooleanstartedfalse;privateScheduledExecutorServiceexecutorService;SystemClock(longperiod){this。periodperiod;this。nowTimenewAtomicLong(System。currentTimeMillis());}publicvoidinitialize(){if(started){return;}this。executorServicenewScheduledThreadPoolExecutor(1,r{ThreadthreadnewThread(r,systemclock);thread。setDaemon(true);returnthread;});executorService。scheduleAtFixedRate(()nowTime。set(System。currentTimeMillis()),this。period,this。period,TimeUnit。MILLISECONDS);Runtime。getRuntime()。addShutdownHook(newThread(this::destroy));startedtrue;}publiclongcurrentTimeMillis(){returnstarted?nowTime。get():System。currentTimeMillis();}publicStringcurrentTime(){returnnewTimestamp(currentTimeMillis())。toString();}publicvoiddestroy(){if(executorService!null){executorService。shutdown();}}}最后 sequence虽然大幅提升了性能,但是在某些情况下仍然可能出现重复的情况,比如机器标识重复、起始时间戳被修改重置等,这些问题需要我们特别注意。 总得来说,sequence被称为分布式高效ID生产黑科技并不为过,著名的ORM框架mybatisplus用的也是这个组件,大家有兴趣也可以去了解一下。 头条创作挑战赛 学习技术,分享技术,期待与大家共同进步,也感谢您的点赞与关注。