雪花算法snoflake

开发的大多数项目并未接触到百万级并发,但是一直以来有个id生成的纠结。我一般就是年月日+随机数生成唯一id,一般来说生成一个备用随机数据,不重复每次一次取值隔天重新从第一个开始取。虽然是可以解决,总感觉这个有点繁琐,仔细查查资料研究了一下,这里有提到uuid、Vesta、Twitter-Snowflake等。从文章中uuid在php中已经有专门的扩展或者写法生成唯一字符串在大并发数据中处理效率还是不够好。其他几中方法有用到数据库……
这对雪花算法我查了查资料发现还是值得操作一番。

雪花算法的基本描述

1.最高位是符号位,始终为0,不可用。 
2.41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
3.10位的机器标识,10位的长度最多支持部署1024个节点。
4.12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

根据以上描述及参考雪花算法及运用PHP基于php的雪花算法等内容我的本地测试
代码如下:

<?php class snowflake
{
const EPOCH_OFFSET = 0; //偏移时间戳
const SIGN = 1;  //最高位树,始终为0,不可用
const TIMESTAMP = 41;  //时间戳位数   默认41位,可以使用69年
const DATA_CENTER = 5;  //IDC编号位数  最多32个节点
const MACHINE_ID = 5;  //机器编号位数  最多32个节点
const SEQUENCE = 12;  //计数序列号位数,即一系列自增id 每个节点每毫秒产生4096个ID序列

protected $data_center_id; //数据中心编号

protected $unix_id;  //机器编号

protected $last_time = null;  //最后一次生成id使用的时间戳

protected $serial = 1;
protected $sign_left_shift = self::TIMESTAMP + self::DATA_CENTER + self::MACHINE_ID + self::SEQUENCE; //符号左位移
protected $time_left_shift = self::DATA_CENTER + self::MACHINE_ID + self::SEQUENCE;  //时间戳左位移
protected $data_center_left_shift = self::MACHINE_ID + self::SEQUENCE; //idc左位移
protected $unix_left_shift = self::SEQUENCE;   //机器编号左位移位数
protected $max_serial = -1 ^ (-1 << self::SEQUENCE);  //最大序列号
protected $max_unix = -1 ^ (-1 << self::MACHINE_ID); //最大机器编号
protected $max_data_center = -1 ^ (-1 << self::DATA_CENTER); //最大数据中心编号

public function __construct($data_center_id, $unix_id)
{
    if ($data_center_id > $this->max_data_center) {
        throw new Exception('数据中心编号取值错误,取值范围为:0-' . $this->max_data_center);
    }
    if ($unix_id > $this->max_unix) {
        throw new Exception('机器编号取值错误,取值范围为:0-' . $this->max_unix);
    }
    $this->data_center_id = $data_center_id;
    $this->unix_id = $unix_id;
}

public function generateId()
{
    $sign = 0;
    $unix_time = $this->getUnixTime();
    //判断时间戳
    if ($unix_time < $this->last_time) {
        throw new Exception('当前时间不能小于最后一次时间!');
    }
    if ($unix_time == $this->last_time) {
        $serial = ++$this->serial;
        if ($serial == $this->max_serial) {
            $unix_time = $this->getUnixTime();
            while ($unix_time <= $this->last_time) {
                $unix_time = $this->getUnixTime();
            }
            $this->serial = 0;
            $serial = ++$this->serial;
        }
    } else {
        $this->serial = 0;
        $serial = ++$this->serial;
    }
    $this->last_time = $unix_time;
    $time = (int)($unix_time - self::EPOCH_OFFSET);
    $id = ($sign << $this->sign_left_shift) | ($time << $this->time_left_shift)
        | ($this->data_center_id << $this->data_center_left_shift)
        | ($this->unix_id << $this->unix_left_shift) | $serial;
    return $id;
}

public function getUnixTime()
{
    return floor(microtime(true) * 1000);
}
}

$data=new snowflake(1,1);
$get_id=$data->generateId();

echo $get_id.PHP_EOL;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值