范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

分布式ID生成系统Leaf的设计思路,源码解读

  小伙伴们好呀,我是 4ye,今天来分享下最近研究的 分布式 ID 生成系统  —— Leaf  ,一起来思考下这个分布式ID的设计吧
  什么是分布式ID?
  ID 最大的特点是  唯一
  而分布式 ID,就是指分布式系统下的 ID,它是  全局唯一  的。 为啥需要分布式ID呢?
  这就和  唯一  息息相关了。
  比如我们用 MySQL 存储数据,一开始数据量不大,但是业务经过一段时间的发展,单表数据每日剧增,最终突破 1000w,2000w …… 系统开始变慢了,此时我们已经尝试了  优化索引 , 读写分离  ,升级硬件 ,升级网络  等操作,但是 单表瓶颈  还是来了,我们只能去 分库分表  了。
  而问题也随着而来了,分库分表后,如果还用  数据库自增ID  的方式的话,那么在用户表中,就会出现 两个不同的用户有相同的ID  的情况,这个是不能接受的。
  而  分布式ID全局唯一  的特点,正是我们所需要的。 分布式ID的生成方式UUID  数据库自增ID (MySQL,Redis)  雪花算法
  基本就上面几种了,UUID 的最大缺点就是太长,36个字符长度,而且无序,不适合。
  而其他两种的缺点还有办法补救,可能这也是 Leaf 提供这两种生成 ID 方式的原因。  项目简介
  Leaf ,分布式 ID 生成系统,有两种生成 ID 的方式:  号段模式  Snowflake模式
  号段模式
  在  数据库自增ID  的基础上进行优化 增加一个  segement  ,减少访问数据库的次数。 双 Buffer 优化,提前缓存下一个 Segement,降低网络请求的耗时(降低系统的TP999指标)
  来自美团技术团队
  biz_tag用来区分业务,max_id表示该biz_tag目前所被分配的ID号段的最大值,step表示每次分配的号段长度
  没优化前,每次都从 db 获取,现在获取的频率和 step 字段相关。
  双 Buffer 优化思路
  号段模式源码解读
  SegmentService 构造方法
  作用  配置 dataSource  设置 MyBatis  实例化 SegmentIDGenImpl  执行 init 方法
  这段代码我也忘了 哈哈,已经多久没直接用 mybatis 了,还是重新去官网翻看的。
  mybatis 官网例子
  实例化 SegmentIDGenImpl 时,其中有两个变量要留意下  SEGMENT_DURATION, 智能调节 step 的关键  cache ,其中 SegmentBuffer 是双 Buffer 的关键设计。
  这里先不展开,看看 init 方法先。
  SegmentIDGenImpl init 方法
  作用  执行 updateCacheFromDb 方法  开后台线程,每分钟执行一次 updateCacheFromDb() 方法
  显然,核心在  updateCacheFromDb  updateCacheFromDb 方法
  这里就直接看源码和我加的注释  private void updateCacheFromDb() {         logger.info("update cache from db");         StopWatch sw = new Slf4JStopWatch();         try {             // 执行 SELECT biz_tag FROM leaf_alloc 语句,获取所有的 业务字段。             List dbTags = dao.getAllTags();             if (dbTags == null || dbTags.isEmpty()) {                 return;             }             // 缓存中的 biz_tag             List cacheTags = new ArrayList(cache.keySet());             // 要插入的 db 中的 biz_tag             Set insertTagsSet = new HashSet<>(dbTags);             // 要移除的缓存中的 biz_tag              Set removeTagsSet = new HashSet<>(cacheTags);              // 缓存中有的话,不用再插入,从 insertTagsSet 中移除             for (int i = 0; i < cacheTags.size(); i++) {                 String tmp = cacheTags.get(i);                 if (insertTagsSet.contains(tmp)) {                     insertTagsSet.remove(tmp);                 }             }                          // 为新增的 biz_tag 创建缓存 SegmentBuffer             for (String tag : insertTagsSet) {                 SegmentBuffer buffer = new SegmentBuffer();                 buffer.setKey(tag);                 Segment segment = buffer.getCurrent();                 segment.setValue(new AtomicLong(0));                 segment.setMax(0);                 segment.setStep(0);                 cache.put(tag, buffer);                 logger.info("Add tag {} from db to IdCache, SegmentBuffer {}", tag, buffer);             }                           // db中存在的,从要移除的 removeTagsSet 移除。             for (int i = 0; i < dbTags.size(); i++) {                 String tmp = dbTags.get(i);                 if (removeTagsSet.contains(tmp)) {                     removeTagsSet.remove(tmp);                 }             }                          // 从 cache 中移除不存在的 bit_tag。             for (String tag : removeTagsSet) {                 cache.remove(tag);                 logger.info("Remove tag {} from IdCache", tag);             }         } catch (Exception e) {             logger.warn("update cache from db exception", e);         } finally {             sw.stop("updateCacheFromDb");         }     }
  执行完后,会出现这样的 log  Add tag leaf-segment-test from db to IdCache, SegmentBuffer SegmentBuffer{key="leaf-segment-test", segments=[Segment(value:0,max:0,step:0), Segment(value:0,max:0,step:0)], currentPos=0, nextReady=false, initOk=false, threadRunning=false, step=0, minStep=0, updateTimestamp=0}
  最后 init 方法结束后,会将 initOk 设置为  true 。
  项目启动完毕后,我们就可以调用这个 API 了。
  如图,访问 LeafController 中的 Segment API,可以获取到一个 id。  SegmentIDGenImpl get 方法
  可以看到,init 不成功会报错。
  以及会直接从 cache 中查找这个 key(biz_tag) , 没有的话会报错。
  拿到这个 SegmentBuffer 时,还得看看它 init 了 没有,没有的话用双检查锁的方式去更新
  先来看下一眼 SegmentBuffer 的结构  SegmentBuffer 类
  ⭐updateSegmentFromDb 方法
  这里就是更新缓存的方法了,主要是更新 Segment 的 value , max,step 字段。
  可以看到有三个 if 分支,下面展开说
  分支一:初始化
  第一次,buffer 还没 init,如上图,执行完后会更新 SegmentBuffer 的 step 和 minStep 字段。  分支二:第二次更新
  这里主要是更新这个 updateTimestamp ,它的作用看分支三
  分支三:剩下的更新
  这里就比较有意思了,就是说如果这个号段在 15分钟 内用完了,那么它会扩大这个 step (不超过 10w),创建一个更大的 MaxId ,降低访问 DB 的频率。
  那么,到这里,我们完成了  updateSegmentFromDb  方法,更新了 Segment 的 value , max,step 字段。
  但是,我们不是每次 get 都走上面的流程,它还得走这个缓存方法  ⭐getIdFromSegmentBuffer 方法
  显然,这是另一个重点。
  如图,在死循环中,先获取读锁,拿到当前的号段 Segment,进行判断  使用超过 10% 就开新线程去更新下一个号段  没超过则将 value (AtomicLong 类型)+1 ,小于 maxId 则直接返回。
  这里要重点留意  读写锁的使用  ,比如 开新线程时,使用了这个 写锁  ,里面的 nextReady 等变量使用了 volatile  修饰
  这里的核心就是切换 Segment。
  至此,号段模式结束。  优缺点
  信息安全  : 如果ID是连续的  ,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。—— 《Leaf——美团点评分布式ID生成系统》
  美团
  可以看到,这个号段模式的最大弊端就是 信息不安全,所以在使用时得三思,能不能用到这些业务中去。  Snowflake模式
  雪花算法,核心就是将 64bit 分段,用来表示时间,机器,序列号等。
  41-bit的时间可以表示(1L<<41)/(1000L   3600   24*365)=69年的时间,10-bit机器可以分别表示1024台机器。
  12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为 2^12 * 1000 = 409.6w/s
  这里使用  Zookeeper  持久顺序节点的特性自动对 snowflake 节点配置 wokerID,不用手动配置。
  时钟回拨问题
  img
  Snowflake模式源码解读
  这部分源码就不一一展开了,直接展示核心代码  SnowflakeZookeeperHolder init 方法
  这里要注意调整这个 connectionTimeoutMs 和 sessionTimeoutMs ,不然两种模式都启动的话,这个 zk 的 session 可能会超时,造成启动失败。
  图中流程  看看 zk 节点存不存在,不存在就创建  同时将 worker id 保存到本地。  创建定时任务,更新 znode。
  znode
  worker Id
  定时任务
  SnowflakeIDGenImpl get 方法
  这里直接看代码和注释了  @Override     public synchronized Result get(String key) {         long timestamp = timeGen();         //  发生了回拨,此刻时间小于上次发号时间         if (timestamp < lastTimestamp) {             long offset = lastTimestamp - timestamp;             if (offset <= 5) {                 try {                     //时间偏差大小小于5ms,则等待两倍时间                     wait(offset << 1);                     timestamp = timeGen();                     //还是小于,抛异常并上报                     if (timestamp < lastTimestamp) {                         return new Result(-1, Status.EXCEPTION);                     }                 } catch (InterruptedException e) {                     LOGGER.error("wait interrupted");                     return new Result(-2, Status.EXCEPTION);                 }             } else {                 return new Result(-3, Status.EXCEPTION);             }         }         if (lastTimestamp == timestamp) {             // sequenceMask = ~(-1L << 12 ) = 4095 二进制即 12 个1             sequence = (sequence + 1) & sequenceMask;             if (sequence == 0) {                 //seq 为0的时候表示是下一毫秒时间开始对seq做随机                 sequence = RANDOM.nextInt(100);                 timestamp = tilNextMillis(lastTimestamp);             }         } else {             //如果是新的ms开始             sequence = RANDOM.nextInt(100);         }         lastTimestamp = timestamp;         // timestampLeftShift = 22, workerIdShift = 12          long id = ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;         return new Result(id, Status.SUCCESS);     }      protected long tilNextMillis(long lastTimestamp) {         long timestamp = timeGen();         while (timestamp <= lastTimestamp) {             timestamp = timeGen();         }         return timestamp;     }      protected long timeGen() {         return System.currentTimeMillis();     } API 效果
  生成 ID
  反解 ID
  至此,这个 Snowflake 模式也了解完毕了。  总结
  看完上面两种模式,我觉得两种模式都有它适用的场景,号段模式更适合对内使用(比如 用户ID),而如果你这个 ID 会被用户看到,暴露出去有其他风险(比如爬虫恶意爬取等),那就得多斟酌了,。而订单号 就更适合用 snowflake 模式。
  分布式ID 的特点  全局唯一  趋势递增  可反解(可选)  信息安全(可选)  参考资料Github 地址:https://github.com/Meituan-Dianping/Leaf/blob/master/README_CN.md  Leaf——美团点评分布式ID生成系统:https://tech.meituan.com/2017/04/21/mt-leaf.html  分布式id生成方案总结:https://www.cnblogs.com/javaguide/p/11824105.html
  喜欢的小伙伴记得关注点点赞哦,全网同名[狗头]

三星将GalaxyS24系列机型减少至2款或应对智能手机需求疲软驱动中国12日讯据TheElec获悉,三星正在审查一项计划,以减少明年推出的GalaxyS24系列的机型数量。按惯例,三星GalaxyS系列支持三款机型,分别是标准版Plus和Ul靠房地产搞经济,就是饮鸩止渴近日房产专家孟晓苏讲2022年中国老百姓存款增加了15万亿,如果拿出三分之一的存款,恢复对房子的购买装修和其他的购买,中国经济就能恢复。央行公布数据!中国人均存款8万元,存款的总量18个月仅拿下2份额,华为鸿蒙系统被高估了?美媒白封锁了众所周知,华为是中国骨头最硬的民企,没有之一。原因无他,就是华为能抗住老美一轮又一轮的制裁,无论是断供操作系统还是断供高端芯片,华为都能很快地找到突破口,并让老美打死华为的计划沦为华为中兴包揽!中国移动公示云资源池数据中心交换机集采从中国移动官网获悉,中国移动日前公示了2023年集中网络云资源池四期工程数据中心交换机及配对路由器的采购结果,华为中兴两家包揽。具体中标详情如下此前,中国移动发布采购公告称,本次共中金12月金融数据解读2022年12月金融数据公布,数据显示,12月新增社融同比少增且略低于市场预期。更值得关注的是,12月新增人民币贷款同比多增,而且企业中长期贷款连续3月大幅放量。本文将从宏观和银行首周流水破亿!DAU持续超百万!50亿级国漫手游更多数据曝光!作为曾经的国漫之光,镇魂街漫画在有妖气累计点击51。66亿次,为全站No。1。动画镇魂街自2016年播出以来,系列全网播放量破10亿次。近日,镇魂街漫画动画双授权的改编手游镇魂街天囤锂不停,千亿锂王天齐锂业超6亿元澳洲买矿,市值较去年高点缩水超40记者于淼1月11日,天齐锂业(002466。SZ)股价下跌1。91,收盘报84。29元股。1月9日,天齐锂业披露今年首单跨境收购案。公告显示,为进一步扩充锂矿资源储备,天齐锂业控股1光年94607亿公里,大得难以想象,有比光年更大的长度单位吗?在日常生活中,我们用到的长度单位是米,千米(公里)。例如,我家离学校不到100米济南到青岛大约350公里。只要我们不离开地球,这两个长度单位基本上够用了。因为地球赤道周长也不过4万收藏!ChinaDaily这30篇写中国文化的文章,值得学习!春天,沿着大运河一路向北,追随春的脚步,体会从江南的草长莺飞到北方的桃花初放。夏天,到嵊泗来一场说走就走的旅行,海风轻拂,渔舟唱晚,任夕阳把你的背影拖得很长很长。秋天,一定要来趟北Doinb再就业?斗鱼获得LPL版权,官方解说也蠢蠢欲动近日,斗鱼直播官方宣布获得2023英雄联盟职业赛事版权一事引起了不少LPL粉丝们的热议。值得一提的是,这是斗鱼直播(房间号200816)在时隔一年之后重获LPL赛事版权,这让不少斗美媒文章美国众议长选举闹剧恐后果严重美国纽约时报网站1月7日刊发题为为什么说麦卡锡艰难当上众议长可能意味着众议院未来将机能失调的文章,作者为埃米莉科克伦。全文摘编如下凯文麦卡锡赢得了美国众议院议长一职,他靠的是屈从于
硬核筑梦!3D动画演示中国空间站建造历程据中国载人航天工程办公室消息,北京时间2022年11月3日9时32分,空间站梦天实验舱顺利完成转位。梦天实验舱转位完成标志着中国空间站T字基本构型在轨组装完成。从空间站的首舱天和核盘点2022年最适合游戏玩家的游戏电视,索尼海信华为前言作为资深游戏玩家,选择一块好的屏幕绝对是关键中的关键。随着游戏设备的普及如今的游戏场景已经不再局限于电脑手机,在客厅使用大屏幕电视进行游戏才是硬核玩家的极致追求。随着双十一的到国产老头乐夕阳红房车来了!好开跑得远,做饭睡觉很舒适看到标题,大家就应该明白了,今天介绍的这款爱旅途夕阳红4号房车,是专为退休人群量身打造的!不仅好开,跑得远,车内空间还宽敞明亮又保温隔音,并且配大水大电,旅行做饭睡觉也很舒适轻松!能源并网技术让空间站三舱时刻电力十足北京日报客户端记者刘苏雅通讯员郭晓峰何雄陈袁11月3日,梦天实验舱成功转位,中国空间站T字基本构型组装完成,将持续在轨运行。不过,空间站供电方式复杂用电需求大,且当航天器太阳能帆板V观财报近视神药等拟禁止网售,有何影响?上市公司密集回应中新经纬11月4日电(张澍楠)国家药监局发布药品网络销售禁止清单(征求意见稿),其中包括政策法规明确禁止销售的药品医疗机构制剂中药配方颗粒等。这对相关上市公司有何影响?会影响其业绩50。6亿元!美国运通旗下连通公司增资获批支付圈消息,近日,中国人民银行同意连通(杭州)技术服务有限公司(以下简称连通公司)注册资本由人民币37亿元增加至人民币50。6亿元,有效期限为长期,许可文件编号为银许准予决字202微信称被未知设备登录纯属误解同一设备可能会显示不同名字Tech星球11月3日消息,近日有网友反馈称,自己的微信被未知设备登录了,还有网友表示自己的微信在半夜显示被登录了,引发热议。对此,微信团队发文表示微信被未知设备登录纯属误解。我的总市值在1。5万亿人民币以上的公司排行榜目前全球主要交易所上市,总市值在1。5万亿人民币以上的公司有39家!国内两家企业上榜!亚马逊市值跌破万亿美元据报道,经历了持续的下跌后,亚马逊最新总市值,两年内首次跌出万亿美元俱乐双杀东部冠军!新赛季最大黑马浮现,他们真的不需要詹姆斯了上赛季东部冠军波士顿凯尔特人到目前为止只拿到了4胜3负的战绩,排在了东部分区第五的位置上,这和赛前外界对于凯尔特人的预期有着很大的差别。然而之所以凯尔特人战绩糟糕,主要开始因为克利云顶S8赛季龙神离场,怪兽来袭,海克斯强化机制继续保留在云顶之弈S7。5赛季,可以说是龙神之弈,虽然一个龙神需要占两个人口,但四龙神成型之后依然能碾压其他阵容。最主要的原因还是敖兴技能过于暴力,而且能直接扫后排,这导致很多英雄没有输出不限购物平台!菜鸟裹裹上线全网运费险,网购退货均可获赔极目新闻记者周丹9块9一份全网运费险,各个电商平台退换货都可用。不仅操作方便,还省了不少运费。作为资深网购一族,武汉张女士近期在使用菜鸟裹裹时有了新体验。目前,菜鸟裹裹服务已覆盖全