最下面代码是mybatisplus根据 李景枫 的开源稍改写的id生成器,核心算法就: Snowflake算法

Snowflake算法核心

把时间戳,工作机器id,序列号组合在一起。

Snowflake算法核心

除了最高位bit标记为不可用以外,其余三组bit占位均可浮动,看具体的业务需求而定。默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1023台机器,序列号支持1毫秒产生4095个自增序列id。下文会具体分析。

Snowflake – 时间戳

这里时间戳的细度是毫秒级,具体代码如下,建议使用64位linux系统机器,因为有vdso,gettimeofday()在用户态就可以完成操作,减少了进入内核态的损耗。

Snowflake – 工作机器id

严格意义上来说这个bit段的使用可以是进程级,机器级的话你可以使用MAC地址来唯一标示工作机器,工作进程级可以使用IP+Path来区分工作进程。如果工作机器比较少,可以使用配置文件来设置这个id是一个不错的选择,如果机器过多配置文件的维护是一个灾难性的事情。

Snowflake – 序列号

序列号就是一系列的自增id(多线程建议使用atomic),为了处理在同一毫秒内需要给多条消息分配id,若同一毫秒把序列号用完了,则“等待至下一毫秒”。

datacenterId是获取的机器网卡然后计算

id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;id = id % (maxDatacenterId + 1);

getMaxWorkerId

StringBuilder mpid = new StringBuilder();mpid.append(datacenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (StringUtils.isNotEmpty(name)) {/** GET jvmPid*/mpid.append(name.split("@")[0]);}/** MAC + PID 的 hashcode 获取16个低位*/return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);

具体算法代码

/*** Copy* right (c) 2011-2020, hubin (jobob@qq.com).* <p>* Licensed under the Apache License, Version 2.0 (the "License"); you may not* use this file except in compliance with the License. You may obtain a copy of* the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the* License for the specific language governing permissions and limitations under* the License.*/
package com.baomidou.mybatisplus.toolkit;import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.concurrent.ThreadLocalRandom;import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;/*** <p>* 分布式高效有序ID生产黑科技(sequence) <br>* 优化开源项目:http://git.oschina.net/yu120/sequence* </p>** @author hubin* @date 2016-08-18*/
public class Sequence {private static final Log logger = LogFactory.getLog(Sequence.class);/* 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) */private final long twepoch = 1288834974657L;private final long workerIdBits = 5L;/* 机器标识位数 */private final long datacenterIdBits = 5L;private final long maxWorkerId = -1L ^ (-1L << workerIdBits);private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);private final long sequenceBits = 12L;/* 毫秒内自增位 */private final long workerIdShift = sequenceBits;private final long datacenterIdShift = sequenceBits + workerIdBits;/* 时间戳左移动位 */private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;private final long sequenceMask = -1L ^ (-1L << sequenceBits);private long workerId;/* 数据标识id部分 */private long datacenterId;private long sequence = 0L;/* 0,并发控制 */private long lastTimestamp = -1L;/* 上次生产id时间戳 */public Sequence() {this.datacenterId = getDatacenterId(maxDatacenterId);this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);}/*** @param workerId     工作机器ID* @param datacenterId 序列号*/public Sequence(long workerId, long datacenterId) {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;}/*** <p>* 获取 maxWorkerId* </p>*/protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {StringBuilder mpid = new StringBuilder();mpid.append(datacenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (StringUtils.isNotEmpty(name)) {/** GET jvmPid*/mpid.append(name.split("@")[0]);}/** MAC + PID 的 hashcode 获取16个低位*/return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);}/*** <p>* 数据标识id部分* </p>*/protected 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();if (null != mac) {id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;id = id % (maxDatacenterId + 1);}}} catch (Exception e) {logger.warn(" getDatacenterId: " + e.getMessage());}return id;}/*** 获取下一个ID** @return*/public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {//闰秒long offset = lastTimestamp - timestamp;if (offset <= 5) {try {wait(offset << 1);timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));}} catch (Exception e) {throw new RuntimeException(e);}} else {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", offset));}}if (lastTimestamp == timestamp) {// 相同毫秒内,序列号自增sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 同一毫秒的序列数已经达到最大timestamp = tilNextMillis(lastTimestamp);}} else {// 不同毫秒内,序列号置为 1 - 3 随机数sequence = ThreadLocalRandom.current().nextLong(1, 3);}lastTimestamp = timestamp;return ((timestamp - twepoch) << timestampLeftShift)    // 时间戳部分| (datacenterId << datacenterIdShift)           // 数据中心部分| (workerId << workerIdShift)                   // 机器标识部分| sequence;                                     // 序列号部分}protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}protected long timeGen() {return SystemClock.now();}}
/*** Copyright (c) 2011-2020, hubin (jobob@qq.com).* <p>* Licensed under the Apache License, Version 2.0 (the "License"); you may not* use this file except in compliance with the License. You may obtain a copy of* the License at* <p>* http://www.apache.org/licenses/LICENSE-2.0* <p>* Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the* License for the specific language governing permissions and limitations under* the License.*/
package com.baomidou.mybatisplus.toolkit;import java.sql.Timestamp;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;/*** <p>* 高并发场景下System.currentTimeMillis()的性能问题的优化* </p>* <p>* System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100倍左右)<br>* System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道<br>* 后台定时更新时钟,JVM退出时,线程自动回收<br>* 10亿:43410,206,210.72815533980582%<br>* 1亿:4699,29,162.0344827586207%<br>* 1000万:480,12,40.0%<br>* 100万:50,10,5.0%<br>* </p>** @author lry*/
public class SystemClock {private final long period;private final AtomicLong now;private SystemClock(long period) {this.period = period;this.now = new AtomicLong(System.currentTimeMillis());scheduleClockUpdating();}private static SystemClock instance() {return InstanceHolder.INSTANCE;}public static long now() {return instance().currentTimeMillis();}public static String nowDate() {return new Timestamp(instance().currentTimeMillis()).toString();}private void scheduleClockUpdating() {ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {@Overridepublic Thread newThread(Runnable runnable) {Thread thread = new Thread(runnable, "System Clock");thread.setDaemon(true);return thread;}});scheduler.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {now.set(System.currentTimeMillis());}}, period, period, TimeUnit.MILLISECONDS);}private long currentTimeMillis() {return now.get();}private static class InstanceHolder {public static final SystemClock INSTANCE = new SystemClock(1);}}

Snowflake id生成器相关推荐

  1. SnowFlake唯一ID生成器

    写在前面 架构是权衡的结果,架构也是一层层的组件拼接起来的,如果没有好用的组件,架构势必会做阉割,架构的理想态是建立在一堆友好.易用.标准化的组件之上的.在我过去的经验中,有两类组件经常会出现在我的架 ...

  2. 基于Twitter的Snowflake算法实现的分布式ID生成器

    /*** 基于Twitter的Snowflake算法实现的分布式ID生成器* ------------------------------------------------------------- ...

  3. ID生成器介绍及著名的五大开源ID生成器的使用UUID,snowflake

    ID在我们日常开发中使用的非常频繁,几乎只要是在开发就会天天打交道,但是你知道吗,ID学问大的很呢,怎么就大了呢,有多大呢?咳咳~~  没开车啊,注意正常,好,接下来我们开始分析分布式ID.别被名字吓 ...

  4. 唯一ID生成器snowflake

    分布式全局唯一ID生成器 很多场景需要使用全局唯一ID,用来标识唯一一条消息,唯一一笔交易,唯一一个用户,唯一一张图片等等. 传统数据库表的自增主键是很简单的一种实现方式,前提是你没有分库,也没有分表 ...

  5. 分布式ID生成器及snowflake(雪花)算法实现

    分布式ID的特点 全局唯一性:不能出现有重复的ID标识,这是基本要求. 递增性:确保生成ID对用户或业务都是递增的. 高可用性:确保任何时候都能生成正确的ID. 高性能性:在高并发的环境下依旧表现良好 ...

  6. Spring Boot 工程集成全局唯一ID生成器 Vesta

    2019独角兽企业重金招聘Python工程师标准>>> 本文内容脑图如下: 文章共 760字,阅读大约需要 2分钟 ! 概 述 在前一篇文章 <Spring Boot工程集成全 ...

  7. 分布式 id 生成器

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 本文来源: www.juejin.im/post/5d8882d8 ...

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

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

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

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

  10. c#分布式ID生成器

    c#分布式ID生成器 简介 这个是根据twitter的snowflake来写的.这里有中文的介绍. 如上图所示,一个64位ID,除了最左边的符号位不用(固定为0,以保证生成的ID都是正数),还剩余63 ...

最新文章

  1. Banknote Dataset(钞票数据集)介绍
  2. 安卓后端mysql_后端Spring Boot+前端Android交互+MySQL增删查改(Java+Kotlin实现)
  3. NYOJ 663 弟弟的作业
  4. mac或者linux磁力下载方法:远离渣雷
  5. VS 2008中的jQuery Intellisense
  6. java工作笔记021---Java设计模式_观察者模式_事件驱动模式
  7. PHP将mysql数据导出为Excel
  8. 使用Maven在jar中包含依赖项
  9. 自然语言处理——文本的表示
  10. JMX实现远程服务器Tomcat系统监控之一
  11. [已解决]安装MPICH2(x64)时显示需要安装NET FRAMEWORK 2.0.50727
  12. android studio调整字体大小,如何在Android Studio中增加字体大小?
  13. 金融二叉树模型-给期权定价
  14. w10系统桌面的计算机找不到,w10桌面我的电脑图标不见了怎么办
  15. 短信工具类——mo信通
  16. 搜狗输入法如何禁用所有快捷键包括系统快键键
  17. roboone机器人_ROBOONE机器人这个品牌怎么样?是否可以加盟投资?
  18. 一个小工具,帮你找到赚钱思路
  19. ffmpeg Intel硬件加速总结
  20. 2022年诺贝尔物理学奖的科学内涵辨识

热门文章

  1. CSS Reset Modern CSS Reset
  2. Java初学01:学习路线,韩顺平java教程百度云
  3. 删除WIN10右键解压缩菜单
  4. cplex java_【CPLEX教程03】java调用cplex求解一个TSP问题模型
  5. Oracle 数据库连接工具
  6. 心法利器[58] | 从长尾问题到以搜代分的机理探索
  7. 游戏平台系统源码开源有多重要
  8. 阿里矢量图标库的使用方法
  9. Eclipse安装SVN插件的方法
  10. 【Python分子动力学】