已掉线,重新登录

首页 > 绿虎论坛 > 历史版块 > 编程 > PHP

标题: php用时间截生成不重复订单号

作者: @Ta

时间: 2019-12-01发布,2022-05-04修改

点击: 4320

<?php
error_reporting(0);
header("Content-type: application/json");

class SnowflakeIdWorker{
    /** 开始时间截 */
    const twepoch = 1651671982333;
    /** 机器id所占的位数 */
    const workerIdBits = 10;
    //支持的最大机器id,结果是1023 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
    const maxWorkerId = (-1 ^ (-1 << self::workerIdBits));
    //序列在id中占的位数
    const sequenceBits = 12;
    //机器ID向左移12位
    const workerIdShift = self::sequenceBits;
    //时间截向左移22位(10+12)
    const timestampLeftShift = self::workerIdBits  + self::sequenceBits;
    //序列号值的最大值,这里为4095 (0b111111111111=0xfff=4095)
    const sequenceMask = (-1 ^ (-1 << self::sequenceBits));
    //工作机器ID(0~1023):默认0
    private $workerId = 0;
    //毫秒内序列(0~4095):标识符,常驻内存
    static $sequence = 0 ;
    //上次生成ID的时间截
    static $lastTimestamp = -1;
    /***
     * 构造函数:设置当前机器id
     * SnowflakeIdWorker constructor.
     * @param $workerId
     */
    public function __construct($workerId)
    {
        //转换类型
        $workerId = (int) $workerId;
        //判断参数合法性
        if($workerId < 0 || $workerId > self::maxWorkerId){
            die('error...');
        }
 
        //设置当前机器id
        $this->workerId = $workerId;
    }
 
 
    public function nextId(){
        //获取当前毫秒时间戳
        $timestamp = $this->timeGen();
        //获取上一次生成id时的毫秒时间戳
        $lastTimestamp = self::$lastTimestamp;
        //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if($timestamp < $lastTimestamp){
            die('error...');
        }
        //如果是同一毫秒内生成的,则进行毫秒序列化
        if($timestamp == $lastTimestamp){
            //获取当前序列号值
            self::$sequence = (self::$sequence + 1) & self::sequenceMask;
            //毫秒序列化值溢出(就是超过了4095)
            if(self::$sequence == 0){
                //阻塞到下一秒,获得新的时间戳
                $timestamp = $this->tilNextMillis($lastTimestamp);
            }
        }else{//如果不是同一毫秒,那么重置毫秒序列化值
            self::$sequence = 0;
        }
        //重置上一次生成的时间戳
        self::$lastTimestamp = $timestamp;
        //移位并通过或运算拼到一起组成64位的ID
        return
            //时间戳左移 22 位
            (($timestamp - self::twepoch) << self::timestampLeftShift) |
            //机器id左移 12 位
            ($this->workerId << self::workerIdShift) |
            //或运算序列号值
            self::$sequence;
    }
    /****
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param $lastTimestamp 上次生成ID的时间截
     * @return float 当前毫秒时间戳
     */
    private function tilNextMillis($lastTimestamp){
        //重新获取当前时间戳
        $timestamp = $this->timeGen();
        //如果等于上一次获取的时间戳,仍然重新获取
        while($timestamp <= $lastTimestamp){
            $timestamp = $this->timeGen();
        }
        //返回新的时间戳
        return $timestamp;
    }
    private function timeGen(){
        return (float)sprintf("%.0f", microtime(true) * 1000);
    }
 
}
//调用
$work1 = new SnowflakeIdWorker(1);
for($i=0; $i<10;$i++) {
    echo $work1->nextId()."\n";
}

[隐藏样式|查看源码]


『回复列表(6|隐藏机器人聊天)』

1. 不是有那个雪花算法么
(/@Ta/2019-12-01 15:11//)

2.

由于多核CPU和负载均衡的存在,两个用户完全可能在同一时间提交订单。

(/@Ta/2019-12-01 17:29//)

3. 通常上 订单号带入用户id就行了
(/@Ta/2019-12-02 01:15//)

4. @老虎会游泳,如何生成高并发不重复订单号
(/@Ta/2019-12-02 01:52//)

5. @幕后导演,百度搜下php雪花算法
(/@Ta/2019-12-02 13:54//)

6. https://learnku.com/articles/32575
(/@Ta/2019-12-02 13:55//)

回复需要登录

6月29日 09:24 星期天

本站由hu60wap6驱动

备案号: 京ICP备18041936号-1