0
点赞
收藏
分享

微信扫一扫

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


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 - 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代码
/**
 **/
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-generatorhttps://github.com/baidu/uid-generatorhttps://github.com/didi/tinyidhttps://github.com/didi/tinyid

如果再不懂,可以参考这两篇文章,有关于雪花算法的详解和优化以及时间回拨问题的解决方案

雪花算法中超级好用的ID生成工具 - 知乎为何用雪花ID?❄ 由于大厂也在用,推特、百度、美团、滴滴等等。redis ❄ 雪花ID是走向分布式架构的垫脚石,若是只会Guid和数据库自增,怎敢说会分布式系统架构。算法 ❄ 雪花ID适合小项目、大项目、超级大项目。…

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

https://zhuanlan.zhihu.com/p/407498038

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

举报

相关推荐

0 条评论