当前位置: 首页 > 科技观察

多种分布式唯一ID生成方案,一次掌握!

时间:2023-03-21 22:05:08 科技观察

上一篇我们讲了一些分库分表相关的基础知识。详情请参考:《用真实业务场景告诉你,高并发下如何设计数据库架构?》。本篇我们继续分库分表的知识,讲讲如何生成全局唯一id。分库分表后必须面对的问题之一就是如何生成id?因为如果一个表分成多个表,每个表的id从1开始累加自增,肯定是错误的。比如你的订单表拆分成1024张订单表,每张表的id从1开始累加,这肯定有问题!您的系统无法根据表的主键查询订单。比如id=50的订单存在于每个表中!所以这个时候就需要一种在分布式架构下生成全局唯一id的方案。分库分表后,对于插入数据库的核心id,不能直接简单的使用表自增id。必须在全局生成一个唯一的id,然后Insert到每张表中,保证每张表中的一个id是全局唯一的。比如order表虽然被拆分成了1024张表,但是id=50的订单只会存在一张表中。那么如何实现全局唯一id呢?有几种选择。(1)方案一:独立库自增id这种方案是指你的系统每生成一个id,就往一个独立库的一个独立表中插入一段没有业务意义的数据,然后获取一个数据库self-递增的ID。添加了一个ID。拿到id后,写入对应的分库分表。比如你有一个auto_id库,里面只有一张表,叫做auto_id表,一个id是自增的。然后每次要获取一个全局唯一id,直接往这张表中插入一条记录就可以获取一个全局唯一id,然后把这个全局唯一id插入到订单的分库分表中即可。这种解决方案的优点是方便简单,任何人都可以使用。缺点是单库生成自增id。并发高了就会有瓶颈,因为auto_id库要承载每秒几万的并发肯定是不现实的。(2)方案二:uuid大家应该都知道,就是用UUID生成一个全局唯一的id。优点是每个系统都是在本地生成的,而不是基于数据库。缺点是uuid太长,作为主键性能太差,不适合作为主键。如果想随机生成一个文件名、编号等,可以使用uuid,但是uuid不能作为主键。(3)方案三:获取系统当前时间该方案是获取当前时间作为全局唯一id。但是问题是,当并发量很高的时候,比如每秒几千个并发,就会出现重复,肯定是不适合的。一般如果采用这种方案,会将当前时间和很多其他业务字段拼接为一个id。如果你觉得在商业上可以接受,那也是可以的。可以将其他业务字段值与当前时间拼接成一个全球唯一的编号,比如一个订单号:时间戳+用户id+业务含义码。(4)方案四:雪花算法思路分析雪花算法是twitter开源的一种分布式id生成算法。核心思想是:用一个64位的长数作为全局唯一id,64位中1位不用,然后41位作为毫秒数,10位作为工作机器id,12位作为序列号。举个例子,比如下面的64位长数,我们看上面的第一部分,是1位:0,这个没有意义,上面的第二部分是41位:表示上面的第三部分timestamp是5bits:代表机房id,10001以上第四部分是5bits:代表机器id,11001以上第五部分是12bits:代表序号是机房的序号同一时间1毫秒内在机房某台机器上生成id,0000000000001位:没有用,为什么?因为二进制第一位是1,那么就是负数,而我们生成的ID都是正数,所以第一位统一是041位:代表时间戳,单位是毫秒。41位最多可以表示2^41-1,即可以识别2^41-1个毫秒值,换算成年就是69年。10位:记录workingmachineid,表示这个服务最多可以部署在2^10台机器上,也就是1024台机器。但是10位中的5位代表机房id,5位代表机器id。意思是最多可以代表2^5个机房(32个机房),每个机房可以代表2^5台机器(32台机器)。12位:用于记录同一毫秒内产生的不同id。12位可以表示的最大正整数是2^12-1=4096,也就是说这12位表示的数可以在同一毫秒内区分4096个不同的ID。生成一个全局唯一的id,然后可以向部署了雪花算法的系统发送请求,雪花算法系统会生成一个唯一的id。这个雪花算法系统首先要知道它所在的机房和机器,比如机房id=17,机器id=12。雪花算法系统收到这个请求后,首先生成一个64位的long型id通过二进制位运算,64位中的第一位无意义。然后41位,可以用当前的时间戳(单位到毫秒),然后用5位设置机房id,用5位设置机器id。最后再判断一下,在当前机房的这台机器的一毫秒内,这是第一次请求,在本次生成id的请求中加上一个序号,作为最后12位。最后一个64位的id出来了,类似于:这个算法可以保证在机房的一台机器上同一毫秒内生成一个唯一的id。一毫秒内可能会产生多个id,但通过最后12位的序号来区分。下面简单看一下这个雪花算法的一个代码实现。这是一个例子。理解了这个意思之后,以后可以尝试自己修改这个算法。简而言之,64位数字中的每一位用于设置不同的标志以区分每个id。(5)雪花算法代码实现publicclassIdWorker{privatelongworkerId;//这表示机器idprivatelongdatacenterId;//这个代表机房idprivatelongsequence;//这表示在一毫秒内生成多个id的最新序列号publicIdWorker(longworkerId,longdatacenterId,longsequence){//workerId完整性检查//这里就检查一下,要求是机房id和你传入的机器id不能超过32,不能小于0if(workerId>maxWorkerId||workerId<0){thrownewIllegalArgumentException(String.format("workerId不能大于%d或小于0",maxWorkerId));}if(datacenterId>maxDatacenterId||datacenterId<0){thrownewIllegalArgumentException(String.format("datacenterId不能大于%d或小于0",maxDatacenterId));}this.workerId=workerId;this.datacenterId=datacenterId;这个.sequence=序列;}privatelongtwepoch=1288834974657L;私人长期工作erIdBits=5L;privatelongdatacenterIdBits=5L;//这是一个二元运算,即5位最多只能有31个数,也就是说机器id只能在32以内privatelongmaxWorkerId=-1L^(-1L<

最新推荐
猜你喜欢