Twitter-SnowFlake算法的产生是源于Twitter为了满足自己业务(每秒上万条消息的请求,每条消息都必须分配一条唯一的id,并且在分布式系统中不同机器产生的id必须不同)的需求。

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

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号) 一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)

snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。

snowflake ID 生成策略图解

0 41bit 51bit 64bit
未使用 timestamp(毫秒) datacenterId+workerId 毫秒内的计数

/**
 * ID 生成策略
 * 毫秒级时间41位+机器ID, 10位+毫秒内序列12位。高位始终为0,表示正数。
 * 0           41     51     64
 * +-----------+------+------+
 * |time       |pc    |inc   |
 * +-----------+------+------+
 *  前41bits是以微秒为单位的timestamp。
 *  接着10bits是事先配置好的机器ID。
 *  最后12bits是累加计数器。
 *  macheine id(10bits)标明最多只能有1024台机器同时产生ID,sequence number(12bits)也标明1台机器1ms中最多产生4096个ID,
 *
 */

PHP代码

class IdWork
{private static $workerId;private static $maxWorkerId = 1023; //最大的机器节点, 2^10 - 1private static $sequence = 0;private static $sequenceMask = 4095; //最大的序列节点, 2^12 - 1private static $workerIdShift = 12; //机器ID左移位数,63 - 51private static $timestampLeftShift = 22; //毫秒时间戳左移位数,63 - 41private static $twepoch = 1288834974657;private static $lastTimestamp = -1;/*** @var self*/private static $self = null;/*** @return self*/public static function getInstance(){if (self::$self == null) {self::$self = new self();}return self::$self;}public function setWorkId($workId){if ($workId > self::$maxWorkerId || $workId < 0) {throw new \Exception("worker Id can't be greater than ".self::$maxWorkerId." or less than 0");}self::$workerId = $workId;return self::$self;}private function timeGen(){//获得当前时间戳$time = explode(' ', microtime());$time2 = substr($time[0], 2, 3);return $time[1] . $time2;}private function tilNextMillis($lastTimestamp){$timestamp = $this->timeGen();while ($timestamp <= $lastTimestamp) {$timestamp = $this->timeGen();}return $timestamp;}public function nextId(){$timestamp = $this->timeGen();//如果存在并发调用,则自增sequenceif (self::$lastTimestamp == $timestamp) {self::$sequence = (self::$sequence + 1) & self::$sequenceMask;//如果sequence自增到4095,也就是4096 & 4095 = 0,重新取时间戳if (self::$sequence == 0) {$timestamp = $this->tilNextMillis(self::$lastTimestamp);}} else {self::$sequence = 0;}if ($timestamp < self::$lastTimestamp) {throw new \Exception("Clock moved backwards.  Refusing to generate id for " . (self::$lastTimestamp - $timestamp) . " milliseconds");}self::$lastTimestamp = $timestamp;if (PHP_INT_SIZE === 4) {return 0;}$nextId = ((sprintf('%.0f', $timestamp) - sprintf('%.0f',self::$twepoch)) << self::$timestampLeftShift) | (self::$workerId << self::$workerIdShift) | self::$sequence;return $nextId;}
}//用法:
$order_no = IdWork::getInstance()->setWorkId($wordId)->nextId();

java代码

/****/
public class SnowFlake {
​/*** 起始时间戳,从2021-12-01开始生成*/private final static long START_STAMP = 1638288000000L;
​/*** 序列号占用的位数 12*/private final static long SEQUENCE_BIT = 12;
​/*** 机器标识占用的位数*/private final static long MACHINE_BIT = 10;
​/*** 机器数量最大值*/private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
​/*** 序列号最大值*/private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
​/*** 每一部分向左的位移*/private final static long MACHINE_LEFT = SEQUENCE_BIT;private final static long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;
​/*** 机器标识*/private long machineId;/*** 序列号*/private long sequence = 0L;/*** 上一次时间戳*/private long lastStamp = -1L;
​/*** 构造方法* @param machineId 机器ID*/public SnowFlake(long machineId) {if (machineId > MAX_MACHINE_NUM || machineId < 0) {throw new RuntimeException("机器超过最大数量");}this.machineId = machineId;}
​/*** 产生下一个ID*/public synchronized long nextId() {long currStamp = getNewStamp();if (currStamp < lastStamp) {throw new RuntimeException("时钟后移,拒绝生成ID!");}
​if (currStamp == lastStamp) {// 相同毫秒内,序列号自增sequence = (sequence + 1) & MAX_SEQUENCE;// 同一毫秒的序列数已经达到最大if (sequence == 0L) {currStamp = getNextMill();}} else {// 不同毫秒内,序列号置为0sequence = 0L;}
​lastStamp = currStamp;
​return (currStamp - START_STAMP) << TIMESTAMP_LEFT // 时间戳部分| machineId << MACHINE_LEFT             // 机器标识部分| sequence;                             // 序列号部分}
​private long getNextMill() {long mill = getNewStamp();while (mill <= lastStamp) {mill = getNewStamp();}return mill;}
​private long getNewStamp() {return System.currentTimeMillis();}
​public static void main(String[] args) {// 订单ID生成测试,机器ID指定第0台SnowFlake snowFlake = new SnowFlake(0);System.out.println(snowFlake.nextId());}
}

大厂也有雪花算法的案例,并且开源,比如百度、滴滴

https://github.com/baidu/uid-generatorhttps://github.com/baidu/uid-generatorhttps://github.com/didi/tinyidhttps://github.com/didi/tinyid如果再不懂,可以参考这两篇文章,有关于雪花算法的详解和优化以及时间回拨问题的解决方案雪花算法中超级好用的ID生成工具 - 知乎为何用雪花ID?❄ 由于大厂也在用,推特、百度、美团、滴滴等等。redis ❄ 雪花ID是走向分布式架构的垫脚石,若是只会Guid和数据库自增,怎敢说会分布式系统架构。算法 ❄ 雪花ID适合小项目、大项目、超级大项目。…https://zhuanlan.zhihu.com/p/407498038

分布式id生成器:彻底解决雪花算法时间回拨问题 - 简书Butterfly 简介 雪花算法是twitter提出的分布式id生成器方案,但是有三个问题,其中前两个问题在业内很常见: 时间回拨问题 机器id的分配和回收问题 机器id的...https://www.jianshu.com/p/7680f88b990b

【黄啊码】百万级别订单量,如何生成唯一订单ID(雪花算法)相关推荐

  1. java生成唯一订单号

    /*** 生成唯一订单号* 规则:四位随机数+"M"+格式化到秒的时间+"R"+六位随机数*/public static String getBillCode( ...

  2. MySQL高并发生成唯一订单号的方法

    高并发下生成唯一订单号的存储过程 这个是用mysql写的存储过程,搭配里面一张数据表使用,达到高并发情况下获得唯一订单号的目的:原理:按照一定规则生成订单号后,把订单号插入数据表后,再返回给用户,由于 ...

  3. 生成唯一订单号 工具类

    package com.jsy.basic.util.utils;import java.text.SimpleDateFormat; import java.util.Date;/*** @prog ...

  4. php订单怎么生成,四种PHP生成唯一订单号的方法

    这几天一直在写个人使用的用户中心,虽然期间遇到不少的问题,但还是一点点的都解决了,也从制作期间学到不少的知识,今天就说一说利用PHP生成订单单的方法. 订单号,大家都不陌生,无论从在网上购物,还是在线 ...

  5. js 根据时间生成唯一订单号

    一般做唯一编号的时候,可以使用guid或者uuid的包直接生成,但是我希望唯一编号能够反应生成的时间信息,所以就准备使用日期+随机值来构造,代码如下: const tradeNo = function ...

  6. 生成唯一订单号_人人皆知却暗藏玄机的“数据库唯一标识符”

    Hello 大家好,今天给大家讲一个几乎所有数据库都支持的特性:生成唯一标识符. 知乎视频​www.zhihu.com 详细内容: 唯一标识符应用场景非常多,比如网站注册时自动给新用户一个唯一 ID, ...

  7. C# 生成唯一订单号

    根据GUID+DateTime.Now.Ticks生产唯一订单号. /// <summary>/// 生成唯一数/// </summary>public class Uniqu ...

  8. php源码单号生成,PHP生成唯一订单号

    在网上找了一番,发现这位同学的想法挺不错的,redtamo,具体的请稳步过去看看,我作简要概述,该方法用上了英文字母.年月日.Unix 时间戳和微秒数.随机数,重复的可能性大大降低,还是很不错的.使用 ...

  9. PHP生成唯一订单号

    $order_number = date('Ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), ...

最新文章

  1. BZOJ3230 相似子串 字符串 SA ST表
  2. Music Notes(前缀和+二分)
  3. 3rd 逻辑运算符的基本用法
  4. ASP.NET AJAX (Atlas) 拖放(Drag Drop)功能6种实现方法总结
  5. python爬虫 被重定向_爬虫篇 | 认识Python最最最常用语重要的库Requests
  6. 模块打包之CommonJS与ES6模块比较初探
  7. linux ftdi 虚拟,linux – 由FTDI USB串行转换器创建的监控(嗅探)/ dev / ttyUSB0
  8. JAVA核心技术36讲教程
  9. 初学者怎样快速学会 SQL
  10. 计算机专业英语词汇1500词
  11. 计算机常见网络故障,网络故障有哪些?常见网络故障处理方法
  12. jclasslib插件
  13. 计算机前端开发论文参考文献,web前端论文参考文献.doc
  14. uniapp点击生成商品海报、下载海报、分享海报
  15. 羽毛球击球技术四大基本环节解码
  16. 编程实战赛来啦!B站周边、高级会员等好礼送你啦!
  17. Zepeto正加速摆脱“捏脸”应用的传统印象,成为亚洲最大虚拟世界平台
  18. google v8 实战 -- 构建v8
  19. 18、HTTP基本操作及源码编译安装 学习笔记
  20. win10系统经常遇到资源管理器卡死

热门文章

  1. P13: * Component组件拆分、子组件向父组件传递数据
  2. CountDownLatch和CyclicBarrier的爱恨情仇
  3. 小方块上升组成背景特效 html+css+js
  4. 简述计算机视觉中的单眼线索,知觉-心理学文章-壹心理
  5. 浅谈SEO的优化问题
  6. SurfaceView打造自定义时钟ClockView
  7. 关于Markdown编辑器
  8. 怎么对BI报表展开测试?
  9. WinForm控件Chart的图表类型
  10. 40 张最全计算机网络基础思维导图