PHP使用SnowFlake算法生成唯一ID

前言
本篇主要介绍高并发算法Snowflake是怎么应用到实战项目中的。
对于怎么理解Snowflake算法,大家可以从网上搜索‘Snowflake’,大量资源可供查看,这里就不一一详诉,这里主要介绍怎么实战应用。
为什么有Snowflake算法的出现呢?
首先它是Twitter提出来的。
前世今生
以前我们可以用UUID作为唯一标识,但是UUID是无序的,又是英文、数字、横杆的结合。当我们要生成有序的id并且按时间排序时,UUID必然不是最好的选择。
当我们需要有序的id时,可以用数据库的自增长id,但是在当今高并发系统时代下,自增长id速度太慢,满足不了需求。然而,对于有‘有序的id按时间排序’这一需求时,Twitter提出了它的算法,并且用于Twitter中。
需要注意的地方
可达并发量根据不同的配置不同,每秒上万并发量不成问题。
id可用时间:69年
使用限制
使用Snowflake其实有个限制,就是必须知道运行中是哪台机器。比如我们用Azure云,配置了10个实例(机器),要知道这10个机器是哪一台。
原理
ID由64bit组成
其中 第一个bit空缺
41bit用于存放毫秒级时间戳
10bit用于存放机器id
12bit用于存放自增ID
除了最高位bit标记为不可用以外,其余三组bit占位均可浮动,看具体的业务需求而定。默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1023台机器,序列号支持1毫秒产生4095个自增序列id。
开始用Snowflake
下面是PHP源码
<?php
namespace App\Services;

abstract class Particle {
    const EPOCH = 1479533469598;
    const max12bit = 4095;
    const max41bit = 1099511627775;

    static $machineId = null;

    public static function machineId($mId = 0) {
        self::$machineId = $mId;
    }

    public static function generateParticle() {
        /*
        * Time - 42 bits
        */
        $time = floor(microtime(true) * 1000);

        /*
        * Substract custom epoch from current time
        */
        $time -= self::EPOCH;

        /*
        * Create a base and add time to it
        */
        $base = decbin(self::max41bit + $time);


        /*
        * Configured machine id - 10 bits - up to 1024 machines
        */
        if(!self::$machineId) {
            $machineid = self::$machineId;
        } else {
            $machineid = str_pad(decbin(self::$machineId), 10, "0", STR_PAD_LEFT);
        }
        
        /*
        * sequence number - 12 bits - up to 4096 random numbers per machine
        */
        $random = str_pad(decbin(mt_rand(0, self::max12bit)), 12, "0", STR_PAD_LEFT);

        /*
        * Pack
        */
        $base = $base.$machineid.$random;

        /*
        * Return unique time id no
        */
        return bindec($base);
    }

    public static function timeFromParticle($particle) {
        /*
        * Return time
        */
        return bindec(substr(decbin($particle),0,41)) - self::max41bit + self::EPOCH;
    }
}

?>

调用方法如下
Particle::generateParticle($machineId);//生成ID
Particle::timeFromParticle($particle);//反向计算时间戳
这里我做了改良 如果机器ID传0 就会去掉这10bit 因为有些时候我们可能用不到这么多ID

评论

此博客中的热门博文

近期折腾 tailscale 的一些心得

Mifare Classic card(M1)卡破解过程

买二手车选择哪个网络平台好

如何防止Cloudflare CDN背后的图片被盗连(Hotlink Protection)?

Swagger 生成 PHP restful API 接口文档

Docker php安装扩展步骤详解

小成本启动跨境电商教程

订单系统设计的思考(分层篇与状态一致篇)

斐讯 N1 部署 Docker 和 OpenWRT,并利用 Hostapd 开启 Wi-Fi 热点

N1小钢炮安装Zerotier 实现远程访问