算法原理

SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

由于在Java中64bit的整数是long类型,所以在Java中SnowFlake算法生成的id就是long来存储的。

SnowFlake可以保证:

所有生成的id按时间趋势递增

整个分布式系统内不会产生重复id(因为有datacenterId和machineId来做区分)

算法实现(Java)

Twitter官方给出的算法实现 是用Scala写的,这里不做分析,可自行查看。

/**

*  * SnowFlake的结构如下(每部分用-分开):

*  * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 -

* 000000000000

*  * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0

*  * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)  *

* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T

* = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69

*  * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId

*  * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

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

*  *

* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生20多万ID左右。

* [email protected] 80000636

*/

public class SnowflakeIdWorker {

private static final Logger logger = LoggerFactory.getLogger(SnowflakeIdWorker.class);

/**

* 起始的时间戳

*/

private final static long START_STMP = 1480166465631L;

/**

* 每一部分占用的位数

*/

private final static long SEQUENCE_BIT = 10; //序列号占用的位数

private final static long MACHINE_BIT = 5; //机器标识占用的位数

private final static long DATACENTER_BIT = 5;//数据中心占用的位数

/**

* 每一部分的最大值

*/

public final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);

public 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;//上一次时间戳

/**

* 根据MAC生成datacenterId,根据MAC + PID生成machineId

*/

public SnowflakeIdWorker() {

long datacenterId = getDatacenterId(MAX_DATACENTER_NUM);

long machineId = getMachineId(datacenterId, MAX_MACHINE_NUM);

check(datacenterId, machineId);

this.datacenterId = datacenterId;

this.machineId = machineId;

}

/**

* datacenterId和machineId可配置

* @param datacenterId

* @param machineId

*/

public SnowflakeIdWorker(long datacenterId, long machineId) {

check(datacenterId, machineId);

this.datacenterId = datacenterId;

this.machineId = machineId;

}

private static void check(long datacenterId, long machineId) {

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

throw new EompRuntimeException(String.format("datacenterId can't be greater than %s or less than 0", MAX_DATACENTER_NUM));

}

if (machineId > MAX_MACHINE_NUM || machineId < 0) {

throw new EompRuntimeException(String.format("machineId can't be greater than %s or less than 0", MAX_MACHINE_NUM));

}

}

/**

* 产生下一个ID

*

* @return

*/

public synchronized long nextId() {

long currStmp = getNewstmp();

if (currStmp < lastStmp) {

throw new EompRuntimeException("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; //序列号部分

}

/**

* 阻塞到下一个毫秒,直到获得新的时间戳

* @return 当前时间戳

*/

private long getNextMill() {

long mill = getNewstmp();

while (mill <= lastStmp) {

mill = getNewstmp();

}

return mill;

}

/**

* 返回以毫秒为单位的当前时间

* @return 当前时间(毫秒)

*/

private long getNewstmp() {

return System.currentTimeMillis();

}

/**

* 机器标识

*/

private static long getMachineId(long datacenterId, long maxWorkerId) {

StringBuilder mpid = new StringBuilder();

mpid.append(datacenterId);

String name = ManagementFactory.getRuntimeMXBean().getName();

if (!name.isEmpty()) {

/** GET jvmPid */

mpid.append(name.split("@")[0]);

}

/** MAC + PID 的 hashcode 获取16个低位 */

return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);

}

/**

* 数据标识id部分

*/

private static long getDatacenterId(long maxDatacenterId) {

long id = 0L;

try {

InetAddress ip = InetAddress.getLocalHost();

NetworkInterface network = NetworkInterface.getByInetAddress(ip);

if (network == null) {

id = 1L;

} else {

byte[] mac = network.getHardwareAddress();

id = ((0x000000FF & (long) mac[mac.length - 1])

| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;

id = id % (maxDatacenterId + 1);

}

} catch (Exception e) {

logger.error("getDatacenterId exception.", e);

}

return id;

}

/*public static void main(String[] args) {

long datacenterId = getDatacenterId(MAX_DATACENTER_NUM);

long machineId = getMachineId(datacenterId, MAX_MACHINE_NUM);

System.out.println("ip:" + datacenterId + ",processId:" + machineId);

}*/

}

测试类:

public class SnowflakeIdWorkerTest {

public static SetidSet = new HashSet<>();

public static void main(String[] args) {

SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(1, 0);

for (long i = 0; i < 1000; i++) {

new Thread(new Worker(snowflakeIdWorker)).start();

}

}

static class Worker implements Runnable {

private SnowflakeIdWorker snowflakeIdWorker;

public Worker(SnowflakeIdWorker snowflakeIdWorker) {

this.snowflakeIdWorker = snowflakeIdWorker;

}

@Override

public void run() {

for (int i = 0; i < 1000; i++) {

Long id = snowflakeIdWorker.nextId();

if (!idSet.add(id)) {

System.err.println("存在重复id:" + id);

}

}

}

}

}

java雪花数据库长度_雪花算法(SnowFlake)Java实现相关推荐

  1. 堆排序算法java左程云_堆排序算法以及JAVA实现

    堆的定义如下: n个元素的序列{k0,k1,...,ki,-,k(n-1)}当且仅当满足下关系时,称之为堆. " ki<=k2i,ki<=k2i+1;或ki>=k2i,ki ...

  2. java数据类型指定长度_判断(2分) Java的各种数据类型占用固定长度,与具体的软硬件平台环境无关...

    参考答案如下 判断多选(3分) 下列有关我国公平责任的源流以及背景的说法正确的是 各定长度单选(2分) Which word stands for "pessimistic"?( ...

  3. java 基础 笔试题_非常全面的java基础笔试题

    1.下列说法正确的是() (程序结构) A java程序的main方法必须写在类甩面 B java程序中可以有多个main方法 C.java程序中类名必须与文件名一样 D.JAVA程序的main方法中 ...

  4. Java ME游戏开发中,碰撞检测算法在Java?ME中的实现(

    2019独角兽企业重金招聘Python工程师标准>>> 在Java ME游戏开发中,碰撞检测算法在Java?ME中的实现(百搜技术) 在Java ME游戏开发中,经常需要进行碰撞检测 ...

  5. python雪花下落代码_雪花算法python实现

    雪花算法-Snowflake Snowflake是Twitter提出来的一个算法,其目的是生成一个64bit的整数: 1bit:一般是符号位,不做处理 41bit:用来记录时间戳,这里可以记录69年, ...

  6. Twitter的分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  7. java青蛙过河打字_趣味算法——青蛙过河(JAVA)

    青蛙过河是一个非常有趣的智力游戏,其大意如下: 一条河之间有若干个石块间隔,有两队青蛙在过河,每队有3只青蛙,这些青蛙只能向前移动,不能向后移动,且一次只能有一只青蛙向前移动.在移动过程中,青蛙可以向 ...

  8. java路由方法_路由选择算法的JAVA实现.doc

    路由选择算法的JAVA实现 路由选择算法的JAVA实现 第15卷第7期电脑开发与应用 路由选择算法的JAVA实现 JAVAImplementationofRouterSelectionAlgorith ...

  9. java引用数组长度_如何引用Java数组的长度

    如何引用Java数组的长度 答:数组名.length 病人先有高热大汗.面赤.口渴饮.脉洪大,后突然出现面色苍白,四肢厥冷,脉微欲绝,此属于() 答:热证转寒 中国大学MOOC: 下列属于病原生物侵染 ...

最新文章

  1. 编程之美——2.7 求最大公约数
  2. python 来搞定 非线性方程组和最小二乘拟合问题
  3. 操作系统之进程管理:4、线程与多线程
  4. LeetCode 题 - 27. 移除元素 python实现
  5. 能量谱与功率谱(转自百度文库与维基百科)
  6. Linux 命令(47)—— file 命令
  7. vue Mutation 必须是同步函数 为什么_为什么vue组件中data必须用函数表达?
  8. Codeforces 448 D. Multiplication Table
  9. python:tushare pro 股票每日行情
  10. adb下载安装教程(已安装Android studio)
  11. LDA模型原理+代码+实操
  12. Maya材质球与渲染基础--Redshift,Arnold,Xgen
  13. MediaType介绍
  14. 如何读博士-2021.06.12
  15. 印度软件外包发展简记
  16. 使用百度翻译开发平台,英文翻译为中文
  17. 超实用的Mac快捷键神器:CheatSheet Mac中文免费版
  18. 小白尝试c++编写飞机大战
  19. PixiJS学习(8)预加载
  20. 计算机公共基础知识总结,计算机公共基础知识总结.docx

热门文章

  1. 在vue页面利用css伪元素::after画样式
  2. Linux下刻录光盘cdrecord
  3. 2013/2014新赛季NBA最期待的
  4. 振荡两周,大饼接近表演时刻
  5. vue 样式冲突解决
  6. Ubuntu-使用惠普打印机
  7. 极智开发 | ubuntu增加虚拟内存swap教程
  8. ppt模板如何制作微立体效果?
  9. 广州质检报告,第三方检验报告需要提供什么资料
  10. 关于php数字转大写人民币的代码(完善)