前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【黄啊码】百万级别订单量,如何生成唯一订单ID(雪花算法)

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

作者头像
黄啊码
发布2022-06-15 14:02:12
6210
发布2022-06-15 14:02:12
举报
文章被收录于专栏:黄啊码【CSDN同名】

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代码

代码语言:javascript
复制
class IdWork
{
    private static $workerId;
    private static $maxWorkerId = 1023; //最大的机器节点, 2^10 - 1
 
    private static $sequence = 0;
    private static $sequenceMask = 4095; //最大的序列节点, 2^12 - 1
 
    private static $workerIdShift = 12; //机器ID左移位数,63 - 51
    private static $timestampLeftShift = 22; //毫秒时间戳左移位数,63 - 41
 
    private 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();
 
        //如果存在并发调用,则自增sequence
        if (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代码

代码语言:javascript
复制
/**
 **/
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 {
            // 不同毫秒内,序列号置为0
            sequence = 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-generator

https://github.com/didi/tinyid

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-01-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档