【雪花算法】PHP生成雪花算法以及测试和使用【原创】

概述

在12.09的时候,生产环境出了个问题,具体原因呢,是因为线上有两个异步任务同时执行,这两个异步任务都是生成几万条数据,然后写表,而表的主键id是用雪花算法生成的,具体是使用公共库里面的SnowFlake.php文件的nextId方法生成的雪花算法ID


大概逻辑如下:

foreach ($checkData as $v) {
   
    $data = [
        'client_id' => $v['client_id'], 
        'activity_tag' => $v['activity_tag'], 
        'id_type' => $v['id_type'], 
        'activity_type' => $v['activity_type'], 
        'update_time' => date("Y-m-d H:i:s")
    ];
    $data['id'] = $snowFlakeModel->nextId();
    $data['activity_tag'] = $v['activity_tag'];
    $data['create_time'] = date("Y-m-d H:i:s");
    $bool = $relationModel->create($data);
    if (!$bool) {
   
        Yii::warning('crowd_relation_info operate failed');
    }
}

这个库的雪花算法在单机下单进程是没问题的,但在单机多进程下是有问题的,有概率出现id重复的问题


测试

为了验证是否确实有问题,我这边写了个PHP脚本来测试,脚本如下:

YIi项目\console\controllers\TestController.php



namespace app\commands;

use app\helper\SnowFlake;
use yii\console\Controller;
use Yii;

/**
 * 测试多进程下全局ID生成算法是否有重复
 * 默认生成的全局ID放到redis的set集合中
 *
 * 原理:
 *  1. 使用雪花算法生成全局ID
 *  2. 使用redis的set集合来存放生成的全局ID,由于set集合里面的元素是唯一的,所以插入的时候失败那么可以认为已经生成过相同的全局ID
 *
 * 使用方法:
 *  1. 进到console目录下
 *  2. 执行命令:php yii test/main {进程数} {每个进程生成的全局ID数量}        主进程开多个子进程去生成全局ID进行测试
 *      * 进程数:不传的话默认是2
 *      * 每个进程生成的全局ID数量:不传的话默认是20000
 *
 * 注意:要去日志里面查看进程的执行结果,如果有重复的话日志里面会输出,但终端下不会输出
 *
 * 3. 使用命令:php yii test/get {数量}    可以查看保存的随机的固定数量的全局ID
 * 4. 使用命令:php yii test/del           删除redis的set集合     【测试完成必须进行删除】
 */
class TestController extends Controller
{
   
    const KEY = 'FIN_PHP:BOLI_TEST_SET';
    protected static $redisObj = null;

    private static function getRedisObj()
    {
   
        if (self::$redisObj) {
   
            return self::$redisObj;
        }
        return Yii::$app->redis;
    }

    /**
     * 主进程开多个子进程去跑
     * @param $n int 进程数
     * @param $num int 每个进程生成的全局ID的数量
     *
     * 注意:默认起两个进程,每个进程生成20000条全局ID
     */
    public function actionMain(int $n = 2, int $num = 20000)
    {
   
        var_dump('这是主进程');
        // 开多个子进程异步去生成全局id
        for ($i = 1; $i <= $n; $i++) {
   
            exec('php ' . __DIR__ . '/../yii test/run ' . $i . ' ' . $num . ' >/dev/null  &', $output, $return);
        }
        var_dump('主进程成功');
    }

    /**
     * 批量生成全局id
     * 生成$num个全局id,保存到redis的set集合中
     */
    public function actionRun($pid = 0, $num = 20000): int
    {
   
        Yii::info("进程:{
     $pid} 开始");

        $snowFlakeModel = new SnowFlake();
        $redis = self::getRedisObj();

        $i = 1;

        while (true

你可能感兴趣的:(PHP,php,算法,开发语言,雪花算法,全局ID)