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

一下子提到了四种分布式ID生成方案,把面试官搞糊涂了

时间:2023-03-20 17:00:56 科技观察

上一篇讲了分库分表相关的一些基础知识。详情请参考:《?支撑日活百万用户的高并发系统,应该如何设计其数据库架构??》。本篇我们继续分库分表的知识,讲讲如何生成全局唯一id。分库分表后必须面对的问题之一就是如何生成id?因为如果一个表分成多个表,每个表的id从1开始累加自增,肯定是错误的。比如你的订单表拆分成1024张订单表,每张表的id从1开始累加,这肯定有问题!您的系统无法根据表的主键查询订单。比如id=50的订单存在于每个表中!所以这时候就需要一种分布式架构下的全局唯一id生成方案。分库分表后,对于插入数据库的核心id,不能直接简单的使用表自增id,必须在全局生成一个唯一的id,然后插入到每张表中,某个id在每个表中保证是全局唯一的。比如order表虽然被拆分成了1024张表,但是id=50的订单只会存在一张表中。那么如何实现全局唯一id呢?有几种选择。方案一:独立数据库自增id这种方案是指你的系统每生成一个id,就往一个独立数据库的独立表中插入一段没有业务意义的数据,然后获取一个自增的id在数据库中。拿到id后,写入对应的分库分表。比如你有一个auto_id库,里面只有一张表,叫做auto_id表,一个id是自增的。然后每次要获取一个全局唯一id,直接往这张表中插入一条记录就可以获取一个全局唯一id,然后把这个全局唯一id插入到订单的分库分表中即可。这种解决方案的优点是方便简单,任何人都可以使用。缺点是单库生成自增id。并发高了就会有瓶颈,因为auto_id库要承载每秒几万的并发肯定是不现实的。方案二:uuid大家应该都知道,就是用UUID生成一个全局唯一的id。优点是每个系统都是在本地生成的,而不是基于数据库。缺点是uuid太长,作为主键性能太差,不适合作为主键。如果想随机生成一个文件名、编号等,可以使用uuid,但是uuid不能作为主键。方案三:获取系统当前时间该方案的意思是获取当前时间作为全局唯一id。但是问题是,当并发量很高的时候,比如每秒几千个并发,就会出现重复,肯定是不适合的。一般如果采用这种方案,会将当前时间和很多其他业务字段拼接为一个id。如果你觉得在商业上可以接受,那也是可以的。可以将其他业务字段值与当前时间拼接成一个全球唯一的编号,比如一个订单号:时间戳+用户id+业务含义码。方案四:雪花算法思路分析雪花算法是twitter开源的一种分布式id生成算法。核心思想是:用一个64位的长数作为全局唯一id,64位中1位不用,然后41位作为毫秒数,10位作为工作机器id,12位作为序列号。我举个例子,比如下面的64位长数,我们看上面的第一部分,是1位:0,这个没有意义。上面的第二部分是41位:代表时间戳。上面第三部分是5位:代表机房id,10001上面第四部分是5位:代表机器id,11001上面第五部分是12位:代表的序号是序号一毫秒内某机房某台机器同时生成的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。雪花算法代码实现publicclassIdWorker{privatelongworkerId;//这表示机器IDprivatelongdatacenterId;//这个代表机房idprivatelongsequence;//表示一毫秒内产生的多个datacenterid的最新序列号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;this.sequence=序列;}privatelongtwepoch=1288834974657L;私人长工IdBits=5L;privatelongdatacenterIdBits=5L;//这是一个二元运算,即5位最多只能有31个数,也就是说机器id只能在32以内privatelongmaxWorkerId=-1L^(-1L<

最新推荐
猜你喜欢