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

分布式全局ID生成器原理剖析及非常齐全开源方案应用示例

  拿我们系统常用Mysql数据库来说,在之前的单体架构基本是单库结构,每个业务表的ID一般从1增,通过 **   AUTO_INCREMENT=1   设置自增起始值,随着系统(比如互联网电商、外卖)用户数据日渐增长,单库性能无法满足业务系统,在这之后我们会使用基于主从同步的读写分离,但当用户量规模连主从模式都无法应对时,我们会采用分库分表(当然现在还有其他解决方案比如分布式关系型数据库如TiDB)的方案,这样对数据分库分表后需要有一个唯一 ID 来标识一条数据或消息,数据库的自增 ID 显然不能满足需求,在复杂分布式系统中,往往还有很多场景需要对大量的数据和消息进行唯一标识,这就迫使我们需要用到分布式系统中全局ID生成器。 我们本篇文章只是介绍一些常用实现方案,而大部分的开源分布式ID生成器基本都是基于号段模式和雪花算法为基础,可以根据不同业务场景需要选择,不做详细说明分布式ID满足要求全局唯一:需要是唯一标识,不能出现重复的 ID 好,这是最基本的要求。  高性能:高QPS、低延迟、否则反倒会成为系统瓶颈  高可用性:可用性接近 5 个 9  信息安全:如果 ID 是连续的对于恶意用户爬虫采用顺序爬取指定 URL爬取信息就非常容易完成;如果是作为订单号就更危险了,可以直接知道一天的单量,所以在一些应用场景下会需要 ID 无规则、不规则的要求  趋势递增:在 MySQL InnoDB 引擎中使用的是聚集索引,采用B+ Tree的数据结构来存储索引数据,在主键的选择上我们应该尽量使用有序的编号保证写入性能  单调递增:保证下一个 ID 一定大于上一个 ID,例如事务版本号、IM 增量消息、排序等特殊需求。  常用解决方案UUID
  全局ID在Java中们可以简单使用来UUID生成,输出的41c9b76fc5ac4265939cd5b27bdacdf1这种结果的字符串数据,可以看生成的是36位长度的16进制的字符串,然后将中划线-替换为空字符串**  public static void main(String[] args) {         String uuid = UUID.randomUUID().toString().replaceAll("-","");        System.out.println(uuid);  }优点优点UUID设计上固然是可以满足全局唯一的要求  缺点UUID太长且无序,在互联网大部分企业中都是使用Mysql数据库,且有些业务场景需要使用到事务因此底层存储引擎采用的是Innodb,这就导致B+ Tree索引的分裂,存储和索引的性能差,并不适合在Innodb作为主键,自增ID比较适合作为Innodb主键  数据库自增ID这样方式就是单独使用一个数据库来生成ID,业务程序通过这个数据库获取ID,表结构可以简单设计如下,--然后再通过事务通过插入等操作数据触发ID自增,这个数据库层级性能比较高,你也可以采用表级别插入返回数据的主键CREATE DATABASE `SEQ_ID`; CREATE TABLE SEQID.SEQUENCE_ID (     id bigint(20) unsigned NOT NULL auto_increment,      id_value char(10) NOT NULL default "",     PRIMARY KEY (id),     UNIQUE KEY id_value(id_value) ) ENGINE=MyISAM;begin     replace into SEQUENCE_ID(id_value) values("xxx");      SELECT LAST_INSERT_ID();     commit; end优点简单、ID自增  缺点DB单点故障  Mysql并发不好,无法抗住高并发  数据库集群模式上面单个数据库有弊端,那么可以采用数据库集群,数据库集群常用主从和主主,我们使用主主模式,每个数据库通过设置不同起始值和相同自增步长来实现,比如三台mysql主主模式,mysql1从1开始自增步长为3,序号1、4、7...,mysql2从2开始自增步长为3,序号2、5、8...,mysql3从3开始自增步长为3,序号3、6、9....,每个业务系统可以通过这三台中获取到IDset @@auto_increment_offset = 1;     -- mysql1起始值 set @@auto_increment_increment = 3;  -- mysql1自增步长  set @@auto_increment_offset = 2;     -- mysql2起始值 set @@auto_increment_increment = 3;  -- mysql2自增步长  set @@auto_increment_offset = 3;     -- mysql3起始值 set @@auto_increment_increment = 3;  -- mysql3自增步长优点解决DB单点问题  缺点不利于扩容,如果需要进行MySQL扩容增加节点还是比较麻烦,可能还需要停机扩容  号段模式号段模式几乎是目前所有开源分布式ID生成器的主流实现方式之一,号段模式比如每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存,不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多。简易版本的表结构如下:CREATE TABLE id_generator (   id int(10) NOT NULL,   max_id bigint(20) NOT NULL COMMENT "当前最大id",   step int(20) NOT NULL COMMENT "号段的步长",   biz_type    int(20) NOT NULL COMMENT "业务类型",   version int(20) NOT NULL COMMENT "版本号",   PRIMARY KEY (`id`) )biz_type :代表不同业务类型max_id :当前最大的可用idstep :代表号段的长度version :是一个乐观锁,每次都更新version,保证并发时数据的正确性
  每次申请一个号段,通过乐观锁的机制兑换   max_id    字段做一次   update   操作,update成功则说明新号段获取成功,新的号段范围是  (max_id ,max_id +step]  update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXXRedis实现
  Redis    也同样可以实现,原理就是利用   redis   **的 **  incr   命令实现ID的原子性自增,redis持久化也支持基于每条命令持久化方式,且redis自身有高可用集群模式 192.168.3.117:6379> set seq_id 1     // 初始化自增ID为1 OK 192.168.3.117:6379> incr seq_id      // 增加1,并返回递增后的数值 (integer) 2雪花算法(SnowFlake)雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法,开源后广受国内大厂的好评,在该算法影响下各大公司相继开发出各具特色的分布式生成器。SnowFlake算法用来生成64位的ID,刚好可以用long整型存储,能够用于分布式系统中生产唯一的ID, 并且生成的ID有序
  Snowflake   生成的是Long类型的ID,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特。
  Snowflake ID组成结构:   正数位   (占1比特)+  时间戳    (占41比特)+  机器ID    (占5比特)+  数据中心    (占5比特)+  自增值    (占12比特),总共64比特组成的一个Long类型。 第一个bit位(1bit):Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。  时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年  工作机器id(10bit):也被叫做     workId    ,这个可以灵活配置,机房或者机器号组合都可以。 序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成4096个ID
  雪花算法比较依赖于时间,会出现时钟回拨的问题,所以尽量保证时间同步,大部分开源分布式ID生成器大都有优化解决时钟回拨的问题
  雪花算法Java实现源码Gitub地址
  下面是基于Twitter的雪花算法SnowFlake,使用Java语言实现,封装成工具方法,各个业务应用可以直接使用该工具方法来获取分布式ID,只需保证每个业务应用有自己的工作机器id即可,而不需要单独去搭建一个获取分布式ID的应用  0 - 41位时间戳 - 5位数据中心标识 - 5位机器标识 - 12位序列号
  5位数据中心标识跟5位机器标识这样的分配仅仅是当前实现中分配的,如果业务有其实的需要,可以按其它的分配比例分配,如10位机器标识,不需要数据中心标识。/**  * twitter的snowflake算法 -- java实现  *   * @author beyond  * @date 2016/11/26  */ public class SnowFlake {      /**      * 起始的时间戳      */     private final static long START_STMP = 1480166465631L;      /**      * 每一部分占用的位数      */     private final static long SEQUENCE_BIT = 12; //序列号占用的位数     private final static long MACHINE_BIT = 5;   //机器标识占用的位数     private final static long DATACENTER_BIT = 5;//数据中心占用的位数      /**      * 每一部分的最大值      */     private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);     private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);     private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);      /**      * 每一部分向左的位移      */     private final static long MACHINE_LEFT = SEQUENCE_BIT;     private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;     private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;      private long datacenterId;  //数据中心     private long machineId;     //机器标识     private long sequence = 0L; //序列号     private long lastStmp = -1L;//上一次时间戳      public SnowFlake(long datacenterId, long machineId) {         if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {             throw new IllegalArgumentException("datacenterId can"t be greater than MAX_DATACENTER_NUM or less than 0");         }         if (machineId > MAX_MACHINE_NUM || machineId < 0) {             throw new IllegalArgumentException("machineId can"t be greater than MAX_MACHINE_NUM or less than 0");         }         this.datacenterId = datacenterId;         this.machineId = machineId;     }      /**      * 产生下一个ID      *      * @return      */     public synchronized long nextId() {         long currStmp = getNewstmp();         if (currStmp < lastStmp) {             throw new RuntimeException("Clock moved backwards.  Refusing to generate id");         }          if (currStmp == lastStmp) {             //相同毫秒内,序列号自增             sequence = (sequence + 1) & MAX_SEQUENCE;             //同一毫秒的序列数已经达到最大             if (sequence == 0L) {                 currStmp = getNextMill();             }         } else {             //不同毫秒内,序列号置为0             sequence = 0L;         }          lastStmp = currStmp;          return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分                 | datacenterId << DATACENTER_LEFT       //数据中心部分                 | machineId << MACHINE_LEFT             //机器标识部分                 | sequence;                             //序列号部分     }      private long getNextMill() {         long mill = getNewstmp();         while (mill <= lastStmp) {             mill = getNewstmp();         }         return mill;     }      private long getNewstmp() {         return System.currentTimeMillis();     }      public static void main(String[] args) {         SnowFlake snowFlake = new SnowFlake(2, 3);          for (int i = 0; i < (1 << 12); i++) {             System.out.println(snowFlake.nextId());         }      } }百度 (Uidgenerator)概述
  官方GitHub地址   ** ** https://github.com/baidu/uid-generator
  UidGenerator是Java实现的, 基于   Snowflake   算法的唯一ID生成器。UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略, 从而适用于   docker   等虚拟化环境下实例自动重启、漂移等场景。 在实现上, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费, 同时对CacheLine补齐,避免了由RingBuffer带来的硬件级「伪共享」问题. 最终单机QPS可达600万。
  依赖版本:   Java8   及以上版本,   MySQL   (内置WorkerID分配器, 启动阶段通过DB进行分配; 如自定义实现, 则DB非必选依赖)
  []( https://github.com/baidu/uid-generator/blob/master/doc/snowflake.png )  Snowflake算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)。默认采用上图字节分配方式:sign(1bit)   ****   固定1bit符号标识,即生成的UID为正数。  delta seconds (28 bits)   **** 当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,而不是毫秒,最多可支持约8.7年  worker id (22 bits)   **** 机器id,最多可支持约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略,同一应用每次重启就会消费一个workId  sequence (13 bits)   **
  **   每秒下的并发序列,13 bits可支持每秒8192个并发。
  UidGenerator    是基于   Snowflake    算法实现的,与原始的   snowflake    算法不同在于,   UidGenerator    支持自   定义时间戳    、   工作机器ID    和  序列号   等各部分的位数,而且  UidGenerator    中采用用户自定义   workId    的生成策略。
  UidGenerator    需要与数据库配合使用,需要新增一个   WORKER_NODE   表。当应用启动时会向数据库表中去插入一条数据,插入成功后返回的自增ID就是该机器的  workId    数据由host,port组成。 提供了两种生成器: DefaultUidGenerator、CachedUidGenerator,如对UID生成性能有要求则使用CachedUidGenerator。CachedUidGenerator
  RingBuffer环形数组,数组每个元素成为一个slot。RingBuffer容量,默认为Snowflake算法中sequence最大值,且为2^N。可通过   boostPower    配置进行扩容,以提高RingBuffer 读写吞吐量。 Tail指针、Cursor指针用于环形数组上读写slot:Tail指针   **** 表示Producer生产的最大序号(此序号从0开始,持续递增)。Tail不能超过Cursor,即生产者不能覆盖未消费的slot。当Tail已赶上curosr,此时可通过   rejectedPutBufferHandler    指定PutRejectPolicy Cursor指针   **
  ** 表示Consumer消费到的最小序号(序号序列与Producer序列相同)。Cursor不能超过Tail,即不能消费未生产的slot。当Cursor已赶上tail,此时可通过   rejectedTakeBufferHandler    指定TakeRejectPolicy
  CachedUidGenerator采用了双RingBuffer,Uid-RingBuffer用于存储Uid、Flag-RingBuffer用于存储Uid状态(是否可填充、是否可消费)由于数组元素在内存中是连续分配的,可最大程度利用CPU cache以提升性能。但同时会带来「伪共享」FalseSharing问题,为此在Tail、Cursor指针、Flag-RingBuffer中采用了CacheLine 补齐方式。
  RingBuffer填充时机  初始化预填充   ****   RingBuffer初始化时,预先填充满整个RingBuffer.  即时填充   ****   Take消费时,即时检查剩余可用slot量(     tail   ** -  cursor    ),如小于设定阈值,则补全空闲slots。阈值可通过  paddingFactor    来进行配置,请参考Quick Start中CachedUidGenerator配置** 周期填充   **
  **   通过Schedule线程,定时补全空闲slots。可通过     scheduleInterval    配置,以应用定时填充功能,并指定Schedule时间间隔 简单使用官方源码导入idea
  建立数据库和导入表WORKER_NODE.sql
  创建一个SpringBoot启动类,在application-dev.yml文件配置数据库信息,启动类配置Mybatis扫描com.baidu.fsg.uid的mapper文件注解,创建一个UidControoler提供一个获取单个uid的接口,启动SpringBoot程序
  访问提供接口地址: http://localhost:8080/uid/snowflake ** ,返回uid结果,每次刷新+1**
  数据库表WORKER_NODE当我们每次启动程序会重新生成新的记录
  美团(Leaf)概述
  官方GitHub地址   ** ** https://github.com/Meituan-Dianping/Leaf  There are no two identical leaves in the world. 世界上没有两片完全相同的树叶。— 莱布尼茨
  Leaf 最早期需求是各个业务线的订单ID生成需求。在美团早期,有的业务直接通过DB自增的方式生成ID,有的业务通过redis缓存来生成ID,也有的业务直接用UUID这种方式来生成ID。以上的方式各自有各自的问题,因此我们决定实现一套分布式ID生成服务来满足需求。
  目前Leaf覆盖了美团点评公司内部金融、餐饮、外卖、酒店旅游、猫眼电影等众多业务线。在4C8G VM基础上,通过公司RPC方式调用,QPS压测结果近5w/s,TP999 1ms  当然,为了追求更高的性能,需要通过RPC Server来部署Leaf 服务,那仅需要引入leaf-core的包,把生成ID的API封装到指定的RPC框架中即可。Leaf Server 是一个spring boot的程序,提供HTTP服务来获取ID。Leaf 提供两种生成的ID的方式(号段模式和snowflake模式),你可以同时开启两种方式,也可以指定开启某种方式(默认两种方式为关闭状态)配置Leaf Server的配置都在leaf-server/src/main/resources/leaf.properties中
  配置项
  含义
  默认值
  leaf.name
  leaf 服务名
  leaf.segment.enable
  是否开启号段模式
  false
  leaf.jdbc.url
  mysql 库地址
  leaf.jdbc.username
  mysql 用户名
  leaf.jdbc.password
  mysql 密码
  leaf.snowflake.enable
  是否开启snowflake模式
  false
  leaf.snowflake.zk.address
  snowflake模式下的zk地址
  leaf.snowflake.port
  snowflake模式下的服务注册端口
  号段模式  如果使用号段模式,需要建立DB表,并配置leaf.jdbc.url, leaf.jdbc.username, leaf.jdbc.password如果不想使用该模式配置leaf.segment.enable=false即可。  Snowflake模式算法取自twitter开源的snowflake算法。  如果不想使用该模式配置leaf.snowflake.enable=false即可。  配置zookeeper地址  在leaf.properties中配置leaf.snowflake.zk.address,配置leaf 服务监听的端口leaf.snowflake.port。  简单使用创建数据库,通过源码根目录下的scripts的leaf_alloc.sql导入数据库表leaf_alloc
  初始化数据,设置步长为2000,每次重启重新获取为下一个号段起始值  INSERT INTO leaf_alloc(biz_tag, max_id, step, DESCRIPTION) VALUES("itxs", 1, 2000, "Test leaf Segment Mode Get Id")
  配置application.properties中的数据库信息,将leaf.segment.enable设置为true或者注释;配置zookeeper信息,leaf.snowflake.enable设置为true或者注释;启动leaf-server Spring Boot启动类
  访问号段模式http接口地址: http://localhost:8080/api/segment/get/itxs
  访问雪花算法的http接口地址: http://localhost:8080/api/snowflake/get/test
  访问监控页面地址: http://localhost:8080/cache
  我们再使用上一小节的工程项目先简单通过将leaf的core模块源码工程引入,使用号段模式,通过@Autowired SegmentIDGenImpl主动注入leaf号段模式实现类,并完成http getSegment测试接口的controllerpackage com.itxs.uiddemo.controller;  import javax.annotation.Resource;  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  import com.baidu.fsg.uid.UidGenerator; import com.sankuai.inf.leaf.Result; import com.sankuai.inf.leaf.segment.SegmentIDGenImpl;  @RestController @RequestMapping(value="/uid") public class UidController {  	@Resource(name = "cachedUidGenerator")     private UidGenerator cachedUidGenerator;  	@Autowired 	private SegmentIDGenImpl idGen;      @GetMapping("/snowflake")     public String snowflake() {         return String.valueOf(this.cachedUidGenerator.getUID());     }        @GetMapping(value = "/segment/{key}")     public Result getSegment(@PathVariable("key") String key) throws Exception {     	return this.idGen.get(key);     }    }
  启动Spring Boot程序,访问 http://localhost:8080/uid/segment/itxs   ,返回data字段就是uid值,每次刷新+1
  重新启动后,再次访问 http://localhost:8080/uid/segment/itxs ,返回data字段1001,也即是新的号段的起始值,数据库的maxid也变为1001
  当然也可以采用Spring Boot Startser方式使用,官网也有相关的说明
  我们自己下载leaf-starter 整合Spring Boot 制作启动器starter源码进行编译
  编译好leaf-boot-starter后我们新建一个Spring Boot demo工程,由于原来封装是基于Spring Boot早期的版本,高版本不兼容,所以用早期版本,由于leaf-boot-starter里面使用zookeeper的客户端curator,我们直接运行是出现curator的某些类找不到,因此我们简单就直接在工程加入curator-framework和curator-recipes的依赖。pom文件<?xml version="1.0" encoding="UTF-8"?>      4.0.0      com.itxs     leaf-spring-boot-demo     1.0-SNAPSHOT              spring-boot-starter-parent         org.springframework.boot         2.0.3.RELEASE                    8         8                                org.springframework.boot             spring-boot-starter-web                               com.sankuai.inf.leaf             leaf-boot-starter             1.0.1-RELEASE                               org.apache.curator             curator-framework             5.2.0                               org.apache.curator             curator-recipes             5.2.0               在class path也即是resource根目录下新建leaf.properties文件,同时开启号段模式和雪花算法,配置信息如下leaf.name=com.sankuai.leaf.opensource.test leaf.segment.enable=true leaf.segment.url=jdbc:mysql://192.168.3.117:3306/leaf?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8 leaf.segment.username=leaf leaf.segment.password=leaf123 leaf.snowflake.enable=true leaf.snowflake.address=192.168.3.117 leaf.snowflake.port=2181新建一个controller用于测试,提供号段和雪花算法测试接口package com.itxs.controller;  import com.sankuai.inf.leaf.common.Result; import com.sankuai.inf.leaf.service.SegmentService; import com.sankuai.inf.leaf.service.SnowflakeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  @RestController @RequestMapping(value="/uid") public class LeafUidController {      @Autowired     private SegmentService segmentService;     @Autowired     private SnowflakeService snowflakeService;      @GetMapping("/snowflake")     public String snowflake() {         return String.valueOf(this.snowflakeService.getId("test"));     }        @GetMapping(value = "/segment/{key}")     public Result getSegment(@PathVariable("key") String key) throws Exception {     	return this.segmentService.getId(key);     } }新建Spring Boot启动类,在启动类上标注@EnableLeafServer开启LeafServer的注解,启动Spring Boot程序,默认是使用8080端口package com.itxs;  import com.sankuai.inf.leaf.plugin.annotation.EnableLeafServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;  @SpringBootApplication @EnableLeafServer public class LeafApplication {     public static void main(String[] args) {         SpringApplication.run(LeafApplication.class,args);     } }
  访问号段uid获取接口: http://localhost:8080/uid/segment/itxs   ,放回id结果如下
  访问雪花算法uid获取接口: http://localhost:8080/uid/snowflake   ,返回id结果如下
  滴滴(TinyID)概述
  官方GitHub地址   ** ** https://github.com/didi/tinyid/
  Tinyid是用Java开发的一款分布式id生成系统,基于数据库号段算法实现,关于这个算法可以参考   美团leaf     或者     tinyid原理介绍   。Tinyid扩展了leaf-segment算法,支持了多db(master),同时提供了java-client(sdk)使id生成本地化,获得了更好的性能与可用性。Tinyid在滴滴客服部门使用,均通过tinyid-client方式接入,每天生成亿级别的id。  性能  http方式访问,性能取决于http server的能力,网络传输速度java-client方式,id为本地生成,号段长度(step)越长,qps越大,如果将号段设置足够大,则qps可达1000w+  可用性  依赖db,当db不可用时,因为server有缓存,所以还可以使用一段时间,如果配置了多个db,则只要有1个db存活,则服务可用使用tiny-client,只要server有一台存活,则理论上可用,server全挂,因为client有缓存,也可以继续使用一段时间  特性全局唯一的long型id趋势递增的id,即不保证下一个id一定比上一个大非连续性提供http和java client方式接入支持批量获取id支持生成1,3,5,7,9...序列的id支持多个db的配置,无单点
  适用场景:只关心id是数字,趋势递增的系统,可以容忍id不连续,有浪费的场景**
  ** 不适用场景:类似订单id的业务(因为生成的id大部分是连续的,容易被扫库、或者测算出订单量)  推荐使用方式tinyid-server推荐部署到多个机房的多台机器  多机房部署可用性更高,http方式访问需使用方考虑延迟问题  推荐使用tinyid-client来获取id,好处如下:  id为本地生成(调用AtomicLong.addAndGet方法),性能大大增加  client对server访问变的低频,减轻了server的压力  因为低频,即便client使用方和server不在一个机房,也无须担心延迟即便所有server挂掉,因为client预加载了号段,依然可以继续使用一段时间 注:使用tinyid-client方式,如果client机器较多频繁重启,可能会浪费较多的id,这时可以考虑使用http方式  推荐db配置两个或更多:  db配置多个时,只要有1个db存活,则服务可用   多db配置   ,如配置了两个db,则每次新增业务需在两个db中都写入相关数据  原理和架构tinyid是基于数据库发号算法实现的,简单来说是数据库中保存了可用的id号段,tinyid会将可用号段加载到内存中,之后生成id会直接内存中产生。  可用号段在第一次获取id时加载,如当前号段使用达到一定量时,会异步加载下一可用号段,保证内存中始终有可用号段。  (如可用号段1-1000被加载到内存,则获取id时,会从1开始递增获取,当使用到一定百分比时,如20%(默认),即200时,会异步加载下一可用号段到内存,假设新加载的号段是1001-2000,则此时内存中可用号段为200-1000,1001~2000),当id递增到1000时,当前号段使用完毕,下一号段会替换为当前号段。依次类推。
  nextId和getNextSegmentId是tinyid-server对外提供的两个http接口  nextId是获取下一个id,当调用nextId时,会传入bizType,每个bizType的id数据是隔离的,生成id会使用该bizType类型生成的IdGenerator。  getNextSegmentId是获取下一个可用号段,tinyid-client会通过此接口来获取可用号段  IdGenerator是id生成的接口  IdGeneratorFactory是生产具体IdGenerator的工厂,每个biz_type生成一个IdGenerator实例。通过工厂,我们可以随时在db中新增biz_type,而不用重启服务  IdGeneratorFactory实际上有两个子类IdGeneratorFactoryServer和IdGeneratorFactoryClient,区别在于,getNextSegmentId的不同,一个是DbGet,一个是HttpGet  CachedIdGenerator则是具体的id生成器对象,持有currentSegmentId和nextSegmentId对象,负责nextId的核心流程。nextId最终通过AtomicLong.andAndGet(delta)方法产生。  简单使用创建表  导入源码根目录下面tinyid/tinyid-server/db.sql的数据库脚本,两张表一张存储每个业务类型的token授权信息,一张存储业务类型ID的号段模式起始值和步长,通过version也即是数据库乐观锁实现原子操作。  cd tinyid/tinyid-server/ && create table with db.sql (mysql)
  配置db  cd tinyid-server/src/main/resources/offline vi application.propertiesdatasource.tinyid.names=primary datasource.tinyid.primary.driver-class-name=com.mysql.jdbc.Driver datasource.tinyid.primary.url=jdbc:mysql://ip:port/databaseName?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8 datasource.tinyid.primary.username=root datasource.tinyid.primary.password=123456启动tinyid-server  将源码放在一个linux主机上,当然得有Jdk和Maven环境,在tinyid-server目录下执行脚本编译并启动编译好的jar包.并启动tinyid-server程序  cd tinyid-server/ sh build.sh offline java -jar output/tinyid-server-xxx.jar
  或者将tinyid源码导入idea中,同样配置db,然后启动tinyid-server
  通过初始化sql脚本中的授权码和biz_type,访问本地的RestApi接口测试,结果如下
  接下来我们使用基于java客户端的方式,这也是官方推荐的,性能最好,我们这里就直接使用客户端源码工程的测试代码导入Maven dependency       com.xiaoju.uemc.tinyid     tinyid-client     ${tinyid.version} 配置客户端信息tinyid_client.properties  tinyid.server=localhost:9999 tinyid.token=0f673adf80504e2eaa552f5d791b644c  #(tinyid.server=localhost:9999/gateway,ip2:port2/prefix,...)编写代码,test为业务类型  Long id = TinyId.nextId("test"); List ids = TinyId.nextId("test", 10);
  我们再看数据库表的信息,发现max_id已经变为200001,也即是每个客户端通过步长申请号段放在内存中,然后更新数据库表为下一次申请id段的起始值
  看到这里,以后如果遇到需要使用分布式ID的场景,你会选择和使用了吗?

中国用20年造成了北斗系统,研发过程中都遭遇过哪些考验?2008年5月12日,四川汶川发生了8级大地震,一时间,通讯中断,震中映秀镇音信全无。20小时后,救援部队突入死亡线,借助北斗系统发出了百字短讯,这是我国的北斗系统首次进入普通民众比尔盖茨宣布离婚,人们对爱情产生了争议当地时间3日,65岁的比尔盖茨与妻子梅琳达离婚,结束了长达27年的婚姻,给出的离婚理由是两人认为继续保持婚姻关系已经无法让彼此继续提升这理由不失风度且专业,说的很高尚,让人敬佩。大30000台制氧机到位后,印度官宣华为中兴不得参与本国5G建设了解更多科技资讯尽在圈聊科技。今天跟大家聊一聊在30000台制氧机到位后,印度官宣华为中兴不得参与本国5G建设。即使面临如今这样的困境,印度始终拉不下脸求助我国,但出于人道主义救援态度大反转!刚送去救援物资,印度就华为中兴问题有了新决定中国5G建设的能力有目共睹,不管是基站建设还是用户数量都迈进世界领先行列,但即便如此,仍然有一些国家犹豫不决,态度反反复复,一开始答应的好好的,美国多久就反悔,印度就是如此。其实,我们支持印度6万呼吸机救命,印度却把5G大单给欧美华为遭剔除印度公布5G电信设备供应商,包括,爱立信,三星和诺基亚等等公司,都可以参与印度5G设备测试。但是,去掉了华为,中兴等公司。同时,印度要求从6月15日开始,印度各个电信商订购电信设备恩将仇报?获得我国帮助后,印度却将中企排除在5G试验之外合作与双赢,可以说是国际关系中的重要基础所在。我国近年来也是一直秉持着开放友善的态度,与许多经济体之间进行了合作,实现了共赢的局面。但是也不乏一些国家,在获得了我国的帮助之后,反而印度公布5G供应商名单,所有中企被排除在外,果然是烂泥扶不上墙最新数据显示,印度单日新增确诊病例超过41万,连续16天新增超过30万例,疫情形势仍在加剧恶化,甚至其派去参加七国集团外长会议的代表团,都被检测出有两名成员感染,足以可见情况之严重五千毫安星空摄影九层散热,骁龙8Gen1仅3399元,高达96好评率目前市面上有很多骁龙8Gen1手机上市了,今年各大手机厂商开始学聪明了,去年因为骁龙888的功耗和发热问题,被很多网友吐槽,口碑也急剧下降,今年的骁龙8Gen1高端机,很多都配备了几素轻薄桌面扇我的专属夏日凉风,小巧易收纳,还能当充电宝转眼已是四月天,南方的天气渐渐热了起来,又到时候给桌面安排风扇的日子了,可能有朋友说吹空调不香吗?空调虽好,但并不是每个角落都能顾及到,特别是像我这种靠窗的地方,更是山长水远,所以防范比特币筑底探底回升,支撑破位下跌才能放量当你的才华还撑不起你的野心时,那就要静下心来学习,当你的能力还驾驭不了你的目标时,那就应该沉下心来历练,只有通过学习交易总结,你才会明白,其实自己真的不是不被市场接纳,只是以前不愿C面向对象编程进阶(函数重载)在非面向对象的过程化语言中,要求每个过程或函数必须具有唯一的调用名,否则会导致编译错误。面向对象程序设计语言提供使用同一函数名的机制,通过参数个数的不同或类型的不同来选择使用相应的
谢谢你,让我从你的世界路过你是不是从心底爱着一个人?无论他是好的坏的,让你开心的难过的。印象里最深的一句话你有多恨一个人,就有多爱一个人。让你念念不忘的人你是有多爱?多恨?多无可奈何?爱情是两个人的事情,只用灵魂与你交换时间用灵魂与你交换时间时间像是一个魔术师给了我惊喜与悲伤,就如这次在我毫无准备的情况下把你带到我的面前。从此我也与你签下了这次的交换书灵魂与时间的交换书。灵魂的内心深处告诉我你不是我最中国男性平均身高,全世界增幅第一,突然冲上热搜第一前段时间,中国成功在东京奥运会上夺得多项赛事的冠军,累计获得38枚金牌。随后,中国运动健儿又在残奥会上取得96枚金牌的好成绩,再一次让世界各国看到了中国的体育实力。不久之后,中国即苏宁全民焕新节悟空榜羽绒服T袖价,ampampquot双11ampampquot末班车别错过!有人说三月不购物,一年的幸福指数都会降低。而就在三月,苏宁推出了315全民焕新节,其中苏宁百货打出春夏焕新的主题,不仅推出了超多的新品,还有超大的折扣贴心的服务,让消费者乐购其中。史上最严KPI?看苏宁小编818如何轻松完成一个亿目标KPI,上班族们最不想听到的词,毕竟和考核待遇以及升迁有关,一旦完不成就意味着这个月白干了一半时间。有一些领导比较苛刻,可能会给手下的员工定一个基本难以完成的KPI,比如昨天苏宁易画质和音效把握好双十一轻松买到万元内的80英寸以上大屏电视国人都比较喜欢大产品,就拿汽车行业来说,奔驰宝马奥迪等品牌旗下的汽车国产后都有加长版车型,在国内受到诸多消费者追捧。而电视行业也是如此,我们也都喜欢大屏电视,因为看着大气,看着爽,提升生活品质来苏宁嗨购五一厨电美容手机一网打尽五一长假马上就要到了,相信很多人已经拟定好计划了,有些人可能会宅在家中,有些人可能去旅游,有些人可能回家陪父母为了满足大家不同的计划,苏宁易购特意启动了嗨购五一,目前已经进入爆发阶高性价比手机大家都知道买手机最主要的是性价比高,用最少的价钱买到最好的性能配置,但那些大老牌子为了赚平民百姓的钱售价实在贵得太离谱了,性价比非常低。那现在我来为大家推荐几款高性价比手机1。在1最不值得买的三款手机第一款华为畅享20Pro,售价1999,在这个价位已经可以买到有晓龙870处理器的红米k40,但在华为畅享20Pro,只给你天机800的处理器,6。5英寸的LCD屏,屏幕已经没有任一个亿小目标!苏宁818这领导比一天要求做50张图还狠犹记得年初的时候,飞越旅行的创始人王小琴女士要求属下一天做出100张图片来,不仅被员工吐槽,更是被全网抨击,认为这是不把员工当人看。现在更狠的来了,正值苏宁易购818,苏宁高管俆金苏宁悟空榜白酒榜,茅台股价破2300,商品加购却被郎酒反超自2020年下半年开始,茅台就出尽了风头,股价屡创新高。今年2月份刚开始,茅台股价就突破了2300元,再次被推上了热搜。事实上,茅台作为白酒板块的龙头股,股价创新高并不算意外,加上