PHP 封装雪花算法

前言:

很多朋友面试的时候都会被问道 百万计或千万级数据应该如何存储,其ID又该如何做到唯一的问题。

自增ID其实有非常多的方法,下面四种是比较常见的

  • 数据库自增ID、比如mongo、mysql;如果使用Mysql的话还可以使用集群模式,不同集群步长不同
  • Redis的incr命令,但是持久化很难保证
  • 雪花算法:时间戳+机器ID+自增ID;可以使用cas
  • 号段模式

我这里就简单介绍下雪花算法的实现方式,具体实现原理并不难,这里便不赘述:

实现:

一般我们封装算法函数都建议放在Servers(服务层)中,这样后续代码量或逻辑复杂的时候就不会像两千年的代码那样难看了。

1. 在服务层中 创建SnowFlakeServe.php文件

<?php

namespace App\Http\Servers;

/**
 * Snowflake 生成唯一ID算法,固定返回19位
 * User: zc
 * Date: 2020/7/22
 * Time: 15:29
 */

define('EPOCH', 1579533469598); // 当前时间(毫秒)
define('NUMWORKERBITS', 10);
define('NUMSEQUENCEBITS', 12);
define('MAXWORKERID', (-1 ^ (-1 << NUMWORKERBITS)));    // 集群ID + 机器ID, 10位,最多支持1024台机器
define('MAXSEQUENCE', (-1 ^ (-1 << NUMSEQUENCEBITS)));  // 序列,12位,每台机器每毫秒内最多产生4096个***
class SnowFlakeServe
{
    private $_lastTimestamp;
    private $_sequence = 0;
    private $_workerId = 1;

    public function __construct($workerId)
    {
        if (($workerId < 0) || ($workerId > MAXWORKERID)) {
            return null;
        }
        $this->_workerId = $workerId;
    }

    public function next()
    {
        $ts = $this->timestamp();
        if ($ts == $this->_lastTimestamp) {
            $this->_sequence = ($this->_sequence + 1) & MAXSEQUENCE;
            if ($this->_sequence == 0) {
                $ts = $this->waitNextMilli($ts);
            }
        } else {
            $this->_sequence = 0;
        }

        if ($ts < $this->_lastTimestamp) {
            return 0;
        }

        $this->_lastTimestamp = $ts;

        $return_pack = $this->pack();
        if (strlen($return_pack) < 13) $return_pack = str_pad($return_pack, 13, '0');
        // return $return_pack;
        return substr($return_pack, 0, 14);
    }

    private function pack()
    {
        return ($this->_lastTimestamp << (NUMWORKERBITS + NUMSEQUENCEBITS)) | ($this->_workerId << NUMSEQUENCEBITS) | $this->_sequence;
    }

    private function waitNextMilli($ts)
    {
        if ($ts = $this->_lastTimestamp) {
            sleep(0.1);
            $ts = $this->timestamp();
        }

        return $ts;
    }

    private function timestamp()
    {
        return $this->millitime() - EPOCH;
    }

    private function millitime()
    {
        $microtime = microtime();
        $comps = explode(' ', $microtime);
        // Note: Using a string here to prevent loss of precision
        // in case of "overflow" (PHP converts it to a double)
        return sprintf('%d%03d', $comps[1], $comps[0] * 1000);
    }
}

2. 在逻辑层中调用方法

        $key = 1;
        $snowflake = new SnowFlakeServe($key);
        $drawal_sn = 'F' . $snowflake->next();

这个示例生成的是订单编号

3. 返回示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值