雪花算法生成分布式ID
分布式主键ID生成方案
分布式主键ID的生成方案有以下几种:
数据库自增主键
缺点:
- 导入旧数据时,可能会ID重复,导致导入失败
- 分布式架构,多个Mysql实例可能会导致ID重复
UUID
缺点:
- 占用空间大
- UUID一般是字符串存储,查询效率低
- 没有排序,无法趋势递增
使用Redis生成ID
缺点:
- 依赖Redis高可用
雪花算法
缺点:
- 依赖服务器时间,如果时间回调,将会导致ID重复
雪花算法原理
雪花算法是 Twitter 开源的主键生成算法 snowflake
它用64位二进制表示主键,由5部分组成:
最高位:0,表示正数
41 位 :表示时间戳,毫秒为单位,最多表示 2^41 -1 毫秒,约69年
10 位 : 前5位用来表示机房ID,后5位表示服务器ID,最多表示 2^5 个机房,和 2^10 个服务器
最后12位:表示序列号,最多表示 2^12-1 = 4096,即每台服务器最多支持每毫秒4096次并发生成

雪花算法的优点:
- 生成效率非常高
- 占用空间相对较少,只用 64 位,即 Long 类型,转换成字符串长度最多19
- 生成的主键趋势递增
雪花算法Java实现
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.Random;
public class SnowflakeIdGenerator {
/**
* 时间戳标识所占二进制位数
*/
private static final int TIME_STAMP_BIT_LEN = 41;
/**
* 机房标识所占二进制位数
*/
private static final int SERVER_ROOM_BIT_LEN = 5;
/**
* 服务器标识所占二进制位数
*/
private static final int SERVER_BIT_LEN = 5;
/**
* 每毫秒中序列所占二进制位数
*/
private static final int SEQ_BIT_LEN = 12;
/**
* 时间戳标识向左移动的位数(这里的1标识最高位)
*/
private static final int TIME_STAMP_LEFT_BIT_LEN = 64 - 1 - TIME_STAMP_BIT_LEN;
/**
* 机房标识左移位数
*/
private static final int SERVER_ROOM_LEFT_BIT_LEN = TIME_STAMP_LEFT_BIT_LEN - SERVER_ROOM_BIT_LEN;
/**
* 服务器标识左移位数
*/
private static final int SERVER_LEFT_BIT_LEN = SERVER_ROOM_LEFT_BIT_LEN - SERVER_BIT_LEN;
/**
* 开始时间戳,此处为 2022年4月9日
*/
private static final long START_TIME_STAMP = 1649497879948L;
/**
* 上次生成ID的时间戳
*/
private static long LAST_TIME_STAMP = -1L;
/**
* 上一次毫秒内存序列值
*/
private static long LAST_SEQ = 0L;
/**
* 获取机房标识(可以手动定义0-31之间的数)
*/
private static final long SERVER_ROOM_ID = getServerRoomId();
/**
* 获取服务器标识(可以手动定义0-31之间的数)
*/
private static final long SERVER_ID = getServerId();
/**
* 机房标识最大值 +1
*/
private static final int SERVER_ROOM_MAX_NUM_1 = ~(-1 << SERVER_ROOM_BIT_LEN) + 1;
/**
* 服务器标识最大值 +1
*/
private static final int SERVER_MAX_NUM_1 = ~(-1 << SERVER_BIT_LEN) + 1;
/**
* 毫秒内存列的最大值
*/
private static final long SEQ_MAX_NUM = ~(-1 << SEQ_BIT_LEN);
/**
* 对服务器地址的哈希码取余作为服务器标识
* TODO 根据实际环境修改该方法,该方法不能应用于开发环境,此处仅作为例子
*
* @return 服务器标识
*/
private static int getServerId() {
try {
String hostAddress = Inet4Address.getLocalHost().getHostAddress();
return (hostAddress.hashCode() & Integer.MAX_VALUE ) % SERVER_MAX_NUM_1;
} catch (UnknownHostException e) {
return new Random().nextInt(SERVER_MAX_NUM_1);
}
}
/**
* 对服务器名称的哈希码取余作为机房标识
* TODO 根据实际环境修改该方法,该方法不能应用于开发环境,此处仅作为例子
*
* @return 机房标识
*/
private static int getServerRoomId() {
try {
String hostName = Inet4Address.getLocalHost().getHostName();
return (hostName.hashCode() & Integer.MAX_VALUE) % SERVER_ROOM_MAX_NUM_1;
} catch (Exception e) {
return new Random().nextInt(SERVER_ROOM_MAX_NUM_1);
}
}
/**
* 一直循环直到获取到下毫秒的时间戳
*
* @param lastMillis
* @return 下一毫秒的时间戳
*/
private static long nextMillis(long lastMillis) {
long now = System.currentTimeMillis();
while (now <= lastMillis) {
now = System.currentTimeMillis();
}
return now;
}
/**
* 生成唯一ID
* 须加锁避免并发问题
*
* @return 返回唯一ID
*/
public synchronized static long generateUniqueId() {
long currentTimeStamp = System.currentTimeMillis();
// 如果当前时间小于上一次ID生成的时间戳,说明系统时间回退过,此时因抛出异常
if (currentTimeStamp < LAST_TIME_STAMP) {
throw new RuntimeException(String.format("系统时间错误! %d 毫秒内拒绝生成雪花ID", START_TIME_STAMP));
}
if (currentTimeStamp == LAST_TIME_STAMP) {
LAST_SEQ = (LAST_SEQ + 1) & SEQ_MAX_NUM;
if (LAST_SEQ == 0) {
currentTimeStamp = nextMillis(LAST_TIME_STAMP);
}
} else {
LAST_SEQ = 0;
}
// 上次生成ID的时间戳
LAST_TIME_STAMP = currentTimeStamp;
return ((currentTimeStamp - START_TIME_STAMP) << TIME_STAMP_LEFT_BIT_LEN | (SERVER_ROOM_ID << SERVER_ROOM_LEFT_BIT_LEN) | (SERVER_ID << SERVER_LEFT_BIT_LEN) | LAST_SEQ);
}
/**
* 主函数测试
*
* @param args
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
int num = 100;
for (int i = 0; i < num; i++) {
System.out.println(generateUniqueId());
}
long end = System.currentTimeMillis();
System.out.println("共生成 " + num + " 个ID,用时 " + (end - start) + " 毫秒");
}
}
雪花算法生成分布式ID的更多相关文章
- 基于雪花算法生成分布式ID(Java版)
SnowFlake算法原理介绍 在分布式系统中会将一个业务的系统部署到多台服务器上,用户随机访问其中一台,而之所以引入分布式系统就是为了让整个系统能够承载更大的访问量.诸如订单号这些我们需要它是全局唯 ...
- 雪花算法【分布式ID问题】【刘新宇】
分布式ID 1 方案选择 UUID UUID是通用唯一识别码(Universally Unique Identifier)的缩写,开放软件基金会(OSF)规范定义了包括网卡MAC地址.时间戳.名字空间 ...
- redis生成分布式id方案
分布式Id - redis方式 本篇分享内容是关于生成分布式Id的其中之一方案,除了redis方案之外还有如:数据库,雪花算法,mogodb(object_id也是数据库)等方案,对于redis来 ...
- 根据twitter的snowflake算法生成唯一ID
C#版本 /// <summary> /// 根据twitter的snowflake算法生成唯一ID /// snowflake算法 64 位 /// 0---0000000000 000 ...
- C# 根据twitter的snowflake算法生成唯一ID
C# 版算法: using System; using System.Collections.Generic; using System.Linq; using System.Text; using ...
- 雪花算法生成ID
前言我们的数据库在设计时一般有两个ID,自增的id为主键,还有一个业务ID使用UUID生成.自增id在需要分表的情况下做为业务主键不太理想,所以我们增加了uuid作为业务ID,有了业务id仍然还存在自 ...
- 使用雪花算法为分布式下全局ID、订单号等简单解决方案考虑到时钟回拨
1.snowflake简介 互联网快速发展的今天,分布式应用系统已经见怪不怪,在分布式系统中,我们需要各种各样的ID,既然是ID那么必然是要保证全局唯一,除此之外,不同当业务还需要不同 ...
- 分布式系统为什么不用自增id,要用雪花算法生成id???
1.为什么数据库id自增和uuid不适合分布式id id自增:当数据量庞大时,在数据库分库分表后,数据库自增id不能满足唯一id来标识数据:因为每个表都按自己节奏自增,会造成id冲突,无法满足需求. ...
- PHP使用SnowFlake算法生成唯一ID
前言:最近需要做一套CMS系统,由于功能比较单一,而且要求灵活,所以放弃了WP这样的成熟系统,自己做一套相对简单一点的.文章的详情页URL想要做成url伪静态的格式即xxx.html 其中xxx考虑过 ...
随机推荐
- 学习廖雪峰的Git教程3--从远程库克隆以及分支管理
一.远程库克隆 这个就比较简单了, git clone git@github.com:****/Cyber-security.git 远程库的地址可以在仓库里一个clone or download的绿 ...
- APUE1--3.8-3.10wirte、read函数以及IO效率
1read函数 #include<unistd.h> ssize_t read(int fd,void* buf,size_t nbytes) 返回值:读到的字节数,若已到文件尾,返回0; ...
- XML的解析方式有哪几种?有什么区别?
有DOM.SAX等. DOM:(Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的一种标准方式. DOM中的核心概念就是节点.DOM在分析XML文档 ...
- 如何使用 Spring Boot 实现异常处理?
Spring 提供了一种使用 ControllerAdvice 处理异常的非常有用的方法. 我们通过实现一个 ControlerAdvice 类,来处理控制器类抛出的所有异常.
- synchronized与Lock、volatile的区别
synchronized与volatile的区别 volatile是线程同步的轻量级实现,因此volatile性能好于synchronized voaltile修饰变量,synchronized修饰方 ...
- 学习MySql(一)
一.安装部署mysql 1.安装mysql: # yum -y install autoconf libaio libaio-devel # groupadd mysql # useradd -r - ...
- JVM的基础知识
一.JVM的基础知识 1.JVM内存结构: 1.JVM堆内存结构: 2.JVM内存分配: 3.Java的堆机构和垃圾回收: 4.Jvm堆内存配置参数: 5.JVM新生代概念和配置: 6.JVM老生代概 ...
- 学习Kvm(六)
五,管理虚拟存储 5.1 虚拟磁盘概述 5.1.1 虚拟化项目中存储的注意事项 [x] 存储的性能几乎总是虚拟化的瓶颈 [x] 通过多个硬盘驱动以分布磁盘I/O来实现存储解决方案 [x] 考虑部署集中 ...
- Contos 安装nodeJs环境
1.去nodeJs官网选择版本: https://nodejs.org/en/download/ 选择64位,右键复制链接地址 https://nodejs.org/dist/v8.12.0/node ...
- Spring官宣网传大漏洞,并提供解决方案
Spring沦陷了!这样的标题这几天是不是看腻了?然而,仔细看看都是拿着之前的几个毫不相干的CVE来大吹特吹.所以,昨天发了一篇关于最近网传的Spring大漏洞的文章,聊了聊这些让人迷惑的营销文.以及 ...