13518219792

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

框架篇:分布式全局唯一ID

框架篇:分布式全局唯一ID

作者:cscw 2021-06-28 14:45:07

开发

架构

分布式 每一次HTTP请求,数据库的事务的执行,我们追踪代码执行的过程中,需要一个唯一值和这些业务操作相关联,对于单机的系统,可以用数据库的自增ID或者时间戳加一个在本机递增值,即可实现唯一值。

创新互联公司成都网站建设按需制作网站,是成都网站建设公司,为成都岗亭提供网站建设服务,有成熟的网站定制合作流程,提供网站定制设计服务:原型图制作、网站创意设计、前端HTML5制作、后台程序开发等。成都网站设计热线:028-86922220

本文转载自微信公众号「潜行前行」,作者cscw。转载本文请联系潜行前行公众号。

前言

每一次HTTP请求,数据库的事务的执行,我们追踪代码执行的过程中,需要一个唯一值和这些业务操作相关联,对于单机的系统,可以用数据库的自增ID或者时间戳加一个在本机递增值,即可实现唯一值。但在分布式,又该如何实现唯一性的ID

分布式ID的特性

数据库自增的ID

实现简单,ID单调自增,数值类型查询速度快,但是单点DB存在宕机风险,无法扛住高并发场景

  
 
 
 
  1. CREATE TABLE FLIGHT_ORDER (
  2.     id int(11) unsigned NOT NULL auto_increment, #自增ID
  3.     PRIMARY KEY (id),
  4. ) ENGINE=innodb;

集群下如何保证数据库ID的唯一性

缺点:不利于后续扩容,如果后续需要扩容还需要人工介入修改 起始值和增长步长

Redis 分布式ID

假如系统有亿万的数据,依靠数据库的自增ID在分表分库之后,需要人工修改每台数据库实例,扩容性差,维护性不好

基于Redis INCR 命令生成分布式全局唯一ID

  
 
 
 
  1. @Autowired
  2. private StringRedisTemplate stringRedisTemplate;
  3. private static final String ID_KEY = "id_good_order";
  4. public Long incrementId() {
  5.     return stringRedisTemplate.opsForValue().increment(ID_KEY);
  6. }

HINCRBY 命令

  
 
 
 
  1. //KEY_NAME 是 hash结构对应的Key,FIELD_NAME 是hash结构的字段,INCR_BY_NUMBER是增量值
  2. redis 127.0.0.1:6379> HINCRBY KEY_NAME FIELD_NAME INCR_BY_NUMBER 

宕机序列号恢复问题

Zookeeper 分布式ID

  
 
 
 
  1.     org.apache.curator
  2.     curator-framework
  3.     4.2.0
  4.     org.apache.curator
  5.     curator-recipes
  6.     4.2.0
  
 
 
 
  1. RetryPolicy retryPolicy = new ExponentialBackoffRetry(500, 3);
  2. CuratorFramework client = CuratorFrameworkFactory.builder()
  3.       .connectString("localhost:2181")
  4.       .connectionTimeoutMs(5000)
  5.       .sessionTimeoutMs(5000)
  6.       .retryPolicy(retryPolicy)
  7.       .build();
  8. client.start();  
  9. String sequenceName = "root/sequence/distributedId";
  10. DistributedAtomicLong  distAtomicLong = new DistributedAtomicLong(client, sequenceName, retryPolicy);
  11. //使用DistributedAtomicLong生成自增序列
  12. public Long sequence() throws Exception {
  13.     AtomicValue sequence = this.distAtomicLong.increment();
  14.     if (sequence.succeeded()) {
  15.         return sequence.postValue();
  16.     } else {
  17.         return null;
  18.     }
  19. }

UUID的优缺点

  
 
 
 
  1. String uuid = UUID.randomUUID().toString().replaceAll("-","");

Twitter 的雪花算法生成分布式ID

Snowflake ID组成结构:正数位(占1比特)+ 时间戳(占41比特)+ 机器ID(占5比特)+ 数据中心(占5比特)+ 自增值(占12比特),总共64比特组成的一个Long类型。

1:第一个bit位(1bit):Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。

2:时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始

3:工作机器id(10bit):也被叫做workId,这个可以灵活配置,机房或者机器号组合都可以。

4:序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成4096个ID

  
 
 
 
  1. //Twitter的SnowFlake算法,使用SnowFlake算法生成一个整数
  2. public class SnowFlakeShortUrl {
  3.     //起始的时间戳
  4.     static long START_TIMESTAMP = 1624698370256L;
  5.     //每一部分占用的位数
  6.     static long SEQUENCE_BIT = 12;   //序列号占用的位数
  7.     static long MACHINE_BIT = 5;     //机器标识占用的位数
  8.     static long DATA_CENTER_BIT = 5; //数据中心占用的位数
  9.     //每一部分的最大值
  10.     static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
  11.     static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
  12.     static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT);
  13.     //每一部分向左的位移
  14.     static long MACHINE_LEFT = SEQUENCE_BIT;
  15.     static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
  16.     static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;
  17.     //dataCenterId + machineId 等于10bit工作机器ID
  18.     private long dataCenterId;  //数据中心
  19.     private long machineId;     //机器标识
  20.     private volatile long sequence = 0L; //序列号
  21.     private volatile long lastTimeStamp = -1L;  //上一次时间戳
  22.     private volatile long l currTimeStamp = -1L; //当前时间戳
  23.     
  24.     private long getNextMill() {
  25.         long mill = System.currentTimeMillis();
  26.         while (mill <= lastTimeStamp) mill = System.currentTimeMillis();
  27.         return mill;
  28.     }
  29.     //根据指定的数据中心ID和机器标志ID生成指定的序列号
  30.     public SnowFlakeShortUrl(long dataCenterId, long machineId) {
  31.         Assert.isTrue(dataCenterId >=0 && dataCenterId <= MAX_DATA_CENTER_NUM,"dataCenterId is illegal!");
  32.         Assert.isTrue(machineId >= 0 || machineId <= MAX_MACHINE_NUM,"machineId is illegal!");
  33.         this.dataCenterId = dataCenterId;
  34.         this.machineId = machineId;
  35.     }
  36.     //生成下一个ID
  37.     public synchronized long nextId() {
  38.         currTimeStamp = System.currentTimeMillis();
  39.         Assert.isTrue(currTimeStamp >= lastTimeStamp,"Clock moved backwards");
  40.         if (currTimeStamp == lastTimeStamp) {
  41.             //相同毫秒内,序列号自增
  42.             sequence = (sequence + 1) & MAX_SEQUENCE;
  43.             if (sequence == 0L) { //同一毫秒的序列数已经达到最大,获取下一个毫秒
  44.                 currTimeStamp = getNextMill();
  45.             }
  46.         } else { 
  47.             sequence = 0L; //不同毫秒内,序列号置为0
  48.         }
  49.         lastTimeStamp = currTimeStamp;
  50.         return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //时间戳部分
  51.                 | dataCenterId << DATA_CENTER_LEFT       //数据中心部分
  52.                 | machineId << MACHINE_LEFT             //机器标识部分
  53.                 | sequence;                             //序列号部分
  54.     }
  55.     
  56.     public static void main(String[] args) {
  57.         SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl(10, 4);
  58.         for (int i = 0; i < (1 << 12); i++) {
  59.             //10进制
  60.             System.out.println(snowFlake.nextId());
  61.         }
  62.     }
  63. }

Reference

[1]github地址:https://github.com/cscsss/learnHome

[2]常见分布式全局唯一ID生成策略及算法的对比:

https://blog.csdn.net/u010398771/article/details/79765836

[3]基于 Redis 的序列号服务(分布式id)的设计:

https://blog.csdn.net/carryxu123456/article/details/82630029

[4]9种 分布式ID生成方案,让你一次学个够:

https://segmentfault.com/a/1190000022717820


当前名称:框架篇:分布式全局唯一ID
链接分享:http://cdbrznjsb.com/article/cceosho.html

其他资讯

让你的专属顾问为你服务