一、分布式ID发号器

要求很明确:不同机器同一时间生成不同ip;同一机器不同时间生成不同IP;

所以根据需求,可选变量有: 机器(网卡、IP)+时间,随机数

二、Why not UUID?

UUID的实现:算法的核心思想是结合机器的网卡、当地时间、一个随机数来生成UUID。

优势:保证唯一性;本地调用,不需要rpc

UUID的缺陷:

1.UUID较长,占用内存空间;往往用字符串表示,作为主键建立索引查询效率低,常见优化方案为“转化为两个uint64整数存储”或者“折半存储”(折半后不能保证唯一性)

2.不具有有序性,无法保证趋势递增

三、依赖DB自增可行么?

实现:自增+设定步长;8个服务节点8个实例(类似取模);

缺陷:

1.不利于服务节点扩充;服务节点固定,步长固定,难以进行水平服务节点的扩展。

2.依赖DB,对数据库造成额外压力

四.全局唯一ID生成器如何设计?考量因素有哪些

1.全局唯一;有序性(不一定连续,但趋势递增);可反解信息;

2.业务性能要求:高性能、高可用

3.接入方式:嵌入式、中心服务发布、rest发布模式

设计一 :ID数据结构

64位

1.机器ID 10位;2的10次方最多可支持1k+台机器;如果每台机器TPS为1W/s,10台服务器则可以有10W/s的TPS,基本满足大部分业务要求。

2.序列号 10位或20位;同理,计算2的20次方,每秒平均可产生百万个ID

3.时间戳 30或40位(分别对应秒级、毫秒级)-时间保证趋势递增

其他可根据业务特点,加上定制的不同生产方式(pom、rest)或版本信息

设计二 并发

为支持大量并发的网络请求,ID生成服务一般采用多线程支持,对于竞争时间和序列(time sequence)采用juc中ReentrantLock或synchronized或atomic变量支持(推荐)

实现

1.定义id实体结构(机器+时间+种子(生成随机数))

//时间戳
private Long time;
//序列号
private Integer sequence;
//生成随机数的种子
private Integer seed;
//机器ip
private Integer clusterIp;

2.根据结构获取对应请求数据,组装实体数据(获取时间戳(毫秒或秒级)os获取本机ip、mac等数据),生成一个不重复的id

//synch保证线程安全
public synchronized long getId()

计算(左移拼装,)【时间戳-初始时间】左移两位集群ip-sequence

3.解析ip,获取对应机器ip、时间戳等信息,则右移取指定位即可

五、Twitter的snowflake算法

Twitter 利用 zookeeper 实现了一个全局ID生成的服务 Snowflake:https://github.com/twitter-archive/snowflake

ID组成:

  • 1位符号位:

由于 long 类型在 java 中带符号的,最高位为符号位,正数为 0,负数为 1,且实际系统中所使用的ID一般都是正数,所以最高位为 0。

  • 41位时间戳(毫秒级):

需要注意的是此处的 41 位时间戳并非存储当前时间的时间戳,而是存储时间戳的差值(当前时间戳 - 起始时间戳),这里的起始时间戳一般是ID生成器开始使用的时间戳,由程序来指定,所以41位毫秒时间戳最多可以使用 (1 << 41) / (1000x60x60x24x365) = 69年

  • 10位数据机器位:

包括5位数据标识位和5位机器标识位,这10位决定了分布式系统中最多可以部署 1 << 10 = 1024 s个节点。超过这个数量,生成的ID就有可能会冲突。

  • 12位毫秒内的序列:

这 12 位计数支持每个节点每毫秒(同一台机器,同一时刻)最多生成 1 << 12 = 4096个ID

加起来刚好64位,为一个Long型。

  • 优点:高性能,低延迟,按时间有序,一般不会造成ID碰撞
  • 缺点:需要独立的开发和部署,依赖于机器的时钟

实现:

public class IdWorker {/*** 起始时间戳 2017-04-01*/private final long epoch = 1491004800000L;/*** 机器ID所占的位数*/private final long workerIdBits = 5L;/*** 数据标识ID所占的位数*/private final long dataCenterIdBits = 5L;/*** 支持的最大机器ID,结果是31*/private final long maxWorkerId = ~(-1L << workerIdBits);/*** 支持的最大数据标识ID,结果是31*/private final long maxDataCenterId = ~(-1 << dataCenterIdBits);/*** 毫秒内序列在id中所占的位数*/private final long sequenceBits = 12L;/*** 机器ID向左移12位*/private final long workerIdShift = sequenceBits;/*** 数据标识ID向左移17(12+5)位*/private final long dataCenterIdShift = sequenceBits + workerIdBits;/*** 时间戳向左移22(12+5+5)位*/private final long timestampShift = sequenceBits + workerIdBits + dataCenterIdBits;/*** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)*/private final long sequenceMask = ~(-1L << sequenceBits);/*** 数据标识ID(0~31)*/private long dataCenterId;/*** 机器ID(0~31)*/private long workerId;/*** 毫秒内序列(0~4095)*/private long sequence;/*** 上次生成ID的时间戳*/private long lastTimestamp = -1L;public IdWorker(long dataCenterId, long workerId) {if (dataCenterId > maxDataCenterId || dataCenterId < 0) {throw new IllegalArgumentException(String.format("dataCenterId can't be greater than %d or less than 0", maxDataCenterId));}if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}this.dataCenterId = dataCenterId;this.workerId = workerId;}/*** 获得下一个ID (该方法是线程安全的)* @return snowflakeId*/public synchronized long nextId() {long timestamp = timeGen();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过,这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}//如果是同一时间生成的,则进行毫秒内序列if (timestamp == lastTimestamp) {sequence = (sequence + 1) & sequenceMask;//毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp = nextMillis(lastTimestamp);}} else {//时间戳改变,毫秒内序列重置sequence = 0L;}lastTimestamp = timestamp;//移位并通过按位或运算拼到一起组成64位的IDreturn ((timestamp - epoch) << timestampShift) |(dataCenterId << dataCenterIdShift) |(workerId << workerIdShift) |sequence;}/*** 返回以毫秒为单位的当前时间* @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}/*** 阻塞到下一个毫秒,直到获得新的时间戳* @param lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/protected long nextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = lastTimestamp;}return timestamp;}
}

实现注意点:

1.由于存在对时进行修改,snowflake算法应对时钟回拨的做法是直接抛出异常。

2.其次是12位序列号溢出的问题,即1ms内请求生成的ID个数超过了2的12次方=4096个id。snowflake算法对此的做法是,等到下一ms再生成ID。

架构设计——ID生成器相关推荐

  1. 游戏开发 数据库ID设计 ID生成器

    游戏开发 数据库ID设计 ID生成器 对于滚服游戏开发,数据库的ID设计非常重要,关乎到合服操作的复杂性.数据库ID设计得好,合服就相当简单.合服主要是数据的合并.把两个或多个单独的服务器数据合并到一 ...

  2. 分布式ID生成器(来源:架构师之路,2017-06-25 58沈剑 架构师之路)

    一.需求缘起 几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如: 消息标识:message-id 订单标识:order-id 帖子标识:tiezi-id 这个记录标识往往就是数据库中的主键,数 ...

  3. 微信序列号生成器架构设计及演变

    一.摘要 微信在立项之初,就已确立了利用数据版本号实现终端与后台的数据增量同步机制,确保发消息时消息可靠送达对方手机,避免了大量潜在的家庭纠纷.时至今日,微信已经走过第五个年头,这套同步机制仍然在消息 ...

  4. 分布式全局ID生成器设计

    项目是分布式的架构,需要设计一款分布式全局ID,参照了多种方案,博主最后基于snowflake的算法设计了一款自用ID生成器.具有以下优势: 保证分布式场景下生成的ID是全局唯一的 生成的全局ID整体 ...

  5. 阅文集团内容中心id生成器的设计与实现

    声明:本文为CSDN原创投稿文章,未经许可,禁止任何形式的转载. 作者:徐海峰(专访),阅文集团首席架构师,主要负责内容中心的网站架构和分布式存储.分布式计算工作.10年互联网开发经验,为开源分布式文 ...

  6. 万亿级调用系统:微信序列号生成器架构设计及演变

    "每天万亿级调用的重量级系统,每次申请序列号平时调用耗时1ms,99.9%的调用耗时小于3ms,服务部署于数百台4核CPU服务器上!" 老司机介绍: 曾钦松,微信高级工程师,目前负 ...

  7. 分布式ID生成器 | 架构师之路

    一.需求缘起 几乎所有的业务系统,都有生成一个唯一记录标识的需求,例如: 消息标识:message-id 订单标识:order-id 帖子标识:tiezi-id 这个记录标识往往就是数据库中的主键,数 ...

  8. 万亿级调用下的优雅——微信序列号生成器架构设计及演变(上)

    版权声明:本文由曾钦松原创文章,转载请注明出处:  文章原文链接:https://www.qcloud.com/community/article/200 来源:腾云阁 https://www.qcl ...

  9. 美团(Leaf)分布式ID生成器,好用的一批!

    不了解分布式ID的同学,先行去看<一口气说出 9种 分布式ID生成方式,面试官有点懵了>温习一下基础知识,这里就不再赘述了 美团(Leaf) Leaf是美团推出的一个分布式ID生成服务,名 ...

  10. im即时通讯源码_IM消息ID技术专题(六):深度解密滴滴的高性能ID生成器(Tinyid)

    1.引言 在中大型IM系统中,聊天消息的唯一ID生成策略是个很重要的技术点.不夸张的说,聊天消息ID贯穿了整个聊天生命周期的几乎每一个算法.逻辑和过程,ID生成策略的好坏有可能直接决定系统在某些技术点 ...

最新文章

  1. netBeans开发j2ME入门一些资源
  2. jQuery基础:下(事件及动画效果)
  3. C++中构造函数和析构函数的调用顺序
  4. 百万奖金池!2022 全球人工智能技术创新大赛—算法挑战赛火热开启
  5. 使用ImitateLogin模拟登录百度
  6. 码上用它开始Flutter混合开发——FlutterBoost
  7. 基础编程题之牛客网星际密码
  8. 大陆集团ADAS招聘丨老司机带你现场体验自动驾驶的快感
  9. 电大计算机网考辅导,【电大复习资料小抄】2016年12月份电大统考计算机考前辅导电子表格 电大考试必备小抄.doc...
  10. php 图片合成,PHP 实现图片合成(两张合并一张)
  11. SaaS行业验尸报告:他们死于资本狂欢前夜
  12. 小程序 - 判断元素是否在页面的显示区域内 wx.createIntersectionObserver
  13. 【技巧】Excel序号设置自动更新
  14. 微信朋友圈背景图片尺寸
  15. PHPExcel的使用
  16. 基于爬虫爬取的数据,完成数据展示网站
  17. 公司 cms 留言管理系统 -- 附源码
  18. C语言求高次方的尾数
  19. BZOJ_3362_[Usaco2004 Feb]Navigation Nightmare 导航噩梦_并查集
  20. 慕容小匹夫 Unity3D移动平台动态读取外部文件全解析

热门文章

  1. [NLP论文阅读]A SIMPLE BUT TOUGH-TO-BEAT BASELINE FOR SENTENCE EMBEDDINGS
  2. 姜启源 数学建模 第十章 软件开发人员的薪金Matlab代码
  3. 用C#语言实现记事本
  4. win10好用的软件推荐
  5. JavaScript学习指南笔记
  6. html如何让图片边缘模糊化,CSS3对图片照片进行边缘模糊处理
  7. E-Prime2.0安装避坑指南
  8. GitHub官网操作指南
  9. SPSS软件自身案例数据
  10. CMMI认证的周期是多久?费用是多少?