基于Twitter ID 生成策略

每秒能生成几十万条 ID

ID 生成要以一种非协作的(uncoordinated)的方式进行,例如不能利用一个全局的原子变量。

ID 大致有序,就是说生成时间相近的两个ID,它们的值也应当相近

按 ID 排序后满足 k-sorted 条件。如果序列 A 要满足 k-sorted,当且仅当对于任意的 p, q,如果 1 <= p <= q - k (1 <= p <= q <= n),则有 A[p] <= A[q]。换句话说,如果元素 p 排在 q 前面,且相差至少 k 个位置,那么 p 必然小于或等于 q。如果ID序列满足这个条件,要获取第 r 条ID之后的记录,只要从第 r - k 条开始查找即可。

解决方案

Twitter 解决这两个问题的方案非常简单高效:每一个 ID 都是 64 位数字,由时间戳、节点号和序列编号组成。其中序列编号是每个节点本地生成的序号,而节点号则由 ZooKeeper 维护。

实现代码

/**

* @author zhujuan

* From: https://github.com/twitter/snowflake

* An object that generates IDs.

* This is broken into a separate class in case

* we ever want to support multiple worker threads

* per process

*/

public class IdWorker {

protected static final Logger LOG = LoggerFactory.getLogger(IdWorker.class);

private long workerId;

private long datacenterId;

private long sequence = 0L;

private long workerIdBits = 5L;

private long datacenterIdBits = 5L;

private long maxWorkerId = -1L ^ (-1L << workerIdBits);

private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

private long sequenceBits = 12L;

private long workerIdShift = sequenceBits;

private long datacenterIdShift = sequenceBits + workerIdBits;

private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

private long sequenceMask = -1L ^ (-1L << sequenceBits);

private long lastTimestamp = -1L;

public IdWorker(long workerId, long datacenterId) {

// sanity check for workerId

if (workerId > maxWorkerId || workerId < 0) {

throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));

}

if (datacenterId > maxDatacenterId || datacenterId < 0) {

throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));

}

this.workerId = workerId;

this.datacenterId = datacenterId;

LOG.info(String.format("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d", timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId));

}

public synchronized long nextId() {

long timestamp = timeGen();

if (timestamp < lastTimestamp) {

LOG.error(String.format("clock is moving backwards. Rejecting requests until %d.", lastTimestamp));

throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));

}

if (lastTimestamp == timestamp) {

sequence = (sequence + 1) & sequenceMask;

if (sequence == 0) {

timestamp = tilNextMillis(lastTimestamp);

}

} else {

sequence = 0L;

}

lastTimestamp = timestamp;

return (timestamp << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;

}

}

基于Instagram 的ID生成策略

生成的ID可以按时间排序

与Twitter需求大致相同

ID最好是64bit的

为了索引更小且方便存储在像Redis这样的系统中

按照某种用户标识进行逻辑分片

解决方案

41bits 存储毫秒格式的时间

10bits 表示逻辑分片ID

原方案是13bits(最多8192个逻辑分片),这里为了与基于Twitter的策略保持大致一致,改成了10bits

12bits 存储自增序列值

原方案是10bits(最多1024个序列),这里为了与基于Twitter的策略保持大致一致,改成了12bits(最多4096个序列)

代码实现

/**

*

* @author Mr_Shang

*

* @version 1.0

*

*/

public class InstaIdGenerator {

protected static final Logger LOG = LoggerFactory.getLogger(IdWorker.class);

/**

* 时间戳的位数,实际占41位,最高位保持为0,保证long值为正数

*/

private int timestampBitCount = 42;

/**

* 逻辑分片位数

*/

private int regionBitCount = 10;

/**

* 逻辑分片的最大数量

*/

private int regionModelVal = 1 << regionBitCount;

/**

* 序列位数

*/

private int sequenceBitCount = 12;

/**

* 总的位数

*/

private int totalBitCount = timestampBitCount + regionBitCount + sequenceBitCount;

/**

* 当前序列值

*/

private long sequence = 0;

/**

* 最后一次请求时间戳

*/

private long lastTimestamp = -1L;

/**

* 序列的位板

*/

private long sequenceMask = -1L ^ (-1L << sequenceBitCount);

/**

* 最后一次请求用户标识

*/

private long lastTag=1L;

public InstaIdGenerator() {

}

public InstaIdGenerator(long seq) {

if (seq < 0) {

seq = 0;

}

this.sequence = seq;

}

public synchronized long nextId(long tag) {

long timestamp = timeGen();

if(tag<0){

tag=-tag;

}

if (timestamp < lastTimestamp) {

LOG.error(String.format("clock is moving backwards. Rejecting requests until %d.", lastTimestamp));

throw new RuntimeException(String.format(

"Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));

}

if (lastTimestamp == timestamp) {

sequence = (sequence + 1) & sequenceMask;

if (sequence == 0) {

timestamp = tilNextMillis(lastTimestamp);

}

} else {

sequence = 0L;

}

if(tag==lastTag){

sequence = (sequence + 1) & sequenceMask;

if (sequence == 0) {

timestamp = tilNextMillis(lastTimestamp);

}

}

lastTag=tag;

lastTimestamp = timestamp;

return (timestamp << (totalBitCount - timestampBitCount))

| ((tag % regionModelVal) << (totalBitCount - timestampBitCount - regionBitCount)) | sequence;

}

}

完整源码地址

项目已经托管到github,飞机在这里☞ 数值型id生成器

参考内容

java id值_Java实现数值型ID生成器相关推荐

  1. java数值型转字符型_Java中数值型,字符型及字符串的相互转换

    Java中数值型,字符型及字符串的相互转换由广州疯狂软件教育java培训分享: 刚开始学习Java不就前些时日被转换问题搞得有点凌乱在这里整理一下. 1.字符型与数值型之间的转换 (1)要将一个整数转 ...

  2. java 判断字符是否为数值型_java算法----判断字符串是否为数值型字符串

    package com.huifudianxia.interview; import java.util.regex.Matcher; import java.util.regex.Pattern; ...

  3. Java范值_Java范型

    定义类的时候,不为类的属性或方法的参数设置具体的类型,只使用一个标记表示,类使用的时候才动态地绑定一种数据类型,这就是范型. 如果应用使用了范型的类时没有指定类型,则会使用默认的类型Object. 1 ...

  4. java 注解值_java 注解默认值操作

    我就废话不多说了,大家还是直接看代码吧~ package com.zejian.annotationdemo; import java.lang.annotation.ElementType; imp ...

  5. jgGrid获得的id值是主键的id而不是jqGrid的行号值

    {name:'cityId',index:'cityId',sorttype:'int',width:0,hidden:true,key:true}, 一定要将你的主键值的的key设置为true,这样 ...

  6. Matlab中布尔值/逻辑值与数值型类型的相互转换

    在涉及到一些下标运算时,经常会遇到布尔值与数值型的相互转换. 数值型转化为逻辑值 在MATLAB中常采用函数logical()将数值型数据转换成逻辑型数据.数值型数据转换成逻辑型的数据一般遵循这样的规 ...

  7. java还值_Java到底是引用传递还是值传递

    前言 前段时间在群里看到类似这样一个问题,下面的代码会输出什么呢? public void test(){ String str = "hello"; change(str); S ...

  8. java默认值_Java中八种基本数据类型的默认值

    通过一段代码来测试一下 8种基本数据类型的默认值 package dierge; public class Ceshi { int a; double b; boolean c; char d; fl ...

  9. java switch 值_Java switch多值匹配操作详解

    这篇文章主要介绍了Java switch多值匹配操作详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 我们都知道 switch 用来走流程分支,大 ...

最新文章

  1. SAP零售:补货 Vs 多步骤补货
  2. 2.6 处理数据不匹配问题-深度学习第三课《结构化机器学习项目》-Stanford吴恩达教授
  3. 前端在linux中常用的命令,前端应该会的23个linux常用命令
  4. java中关于IO流的知识总结(重点介绍文件流的使用)
  5. Django中的反向解析
  6. oracle audit文件,[20191128]oracle Audit文件管理2.txt
  7. 平时喜欢使用的软件总结 欲善其事,必先利其器
  8. Win32 控件篇(2)
  9. pt5 mysql预处理_技术分享 | MySQL 监控利器之 Pt-Stalk
  10. ext2文件系统了解
  11. 漫画 | 为什么美国人发明了互联网?
  12. Typora官网下载慢,用这个镜像
  13. 最好用的Mac任务规划软件【滴答清单】
  14. boj 1348 网络流,从来没有一个网络流能让我如此泪流满面,这样的建图方式,仰慕dalong
  15. numpy.ones用法
  16. windows 安装vagrant reload 失败; No Virtualbox Guest Additions installation found.
  17. 持续造风,快手为品牌、商家提供“保姆式”服务
  18. p-sum结构解释+代码 二叉区间树
  19. 基类、派生类、虚基类、虚函数、虚析构、纯虚函数、抽象类
  20. 【蓝桥杯 路径 python】Dij算法

热门文章

  1. Windows IIS服务器安装(超详细)
  2. 在python集成开发环境中、可使用快捷键运行程序_在Python集成开发环境中,可使用快捷键 运行程序。_学小易找答案...
  3. 关于调用新浪支付接口
  4. 精编,精讲,精练,精益求精---AP微积分第10版在精雕细琢反复打磨中与你相遇
  5. 2021-4-8应用
  6. d3 - 建立力引导图将知识图谱可视化 (一)
  7. (转)智能投顾这么干才靠谱!BondIT获复星B轮投资
  8. GO语言中的循环语句
  9. 正反馈+负反馈还不够,还有【中性反馈】
  10. 【目标检测】|MicroNet