php 全局唯一id,探索 PHP 如何生成全局唯一的 id

探索开始

1. 基于时间+随机码生成id

php提供了一个生成唯一值生成函数uniqid($prefix,$more_entropy),这是一个基于毫秒级时间生成id的函数,不带参数执行输出13位字符随机码,$prefix返回随机码的前缀,$more_entropy设为true时为加熵,返回字符会变为23位(不包括$prefix)。编辑代码如下:

单进程版代码

$start_time = microtime(true);

$container = [];

$all_count = 1000000; //生成id总数量(一百万个)

for ($i = 0; $i < $all_count; $i++) {

//生成随机id

$random = uniqid(); //长这样子:5e1401c70b7db

$container[$random] = $i; //去掉重复id

}

$waste_time = bcsub(microtime(true), $start_time, 4);//计算时间

$conclusion = '耗时:' . $waste_time . 's,共生成id个数:' . $all_count . ',重复id个数:' . ($all_count - count($container));

dump($conclusion);

执行代码,返回

1190a1527c360732838921ce176ff82c.png

多次重复执行,发现重复id都是0,耗时都在60秒以上,平均每秒可以生成一两万个id,感觉uniqid()不加参数也不错嘛,但是这是幻觉而已,坏得很,经不起考验的。

多进程版代码

$start_time = microtime(true);

$process_count = 50; //进程数量

$all_count = 1000000; //生成id总数量(一百万个)

$per_process_count = floor($all_count / $process_count); //每个进程生成id总数量

for ($i = 0; $i < $process_count; $i++) {

//开多进程模拟真实场景

MultiProcessHelper::instance($process_count)->multiProcessTask(function () use ($per_process_count, $i) {

//生成随机id

$container = [];

for ($j = 0; $j < $per_process_count; $j++) {

$random = uniqid(); //长这样子:5e1401c70b7db

$container[] = $random;

}

//把生成的id放到缓存里面

Cache::set($i, $container);

});

}

MultiProcessHelper::recycleProcess();//回收子进程

//以下是整理多个并发进程生成id的重复情况

$container = [];

for ($i = 0; $i < $process_count; $i++) {

$res = Cache::get($i);

foreach ($res as $v) {

$container[$v] = 0; //去掉重复id

}

}

$waste_time = bcsub(microtime(true), $start_time, 4);//计算时间

$conclusion = '耗时:' . $waste_time . 's,共生成id个数:' . $all_count . ',重复id个数:' . ($all_count - count($container));

dump($conclusion);

执行代码,返回

237b6fe008edbbdd3259aba132013955.png

结果发现时间变短了,平均每秒生成的id数量达到了10万个。但是别忘了,异步执行任务的进程是50个,榨干了cpu了。再看,重复个数不再为0,而是有10万个之多,大概1/10都重复了,所以不加熵使用uniqid不能保证生成唯一id。

如果设置$prefix为mt_rand(),mt_rand()会生成0到最大随机数数(21亿左右)的随机整数,$more_entropy=true,那样将返回更加随机的id,利用这一点设计随机码生成规则。编辑代码如下:

$random = uniqid(mt_rand(), true); //长这样子:21083019475e14021cdceb11.02999582

单进程版执行结果

93144bef711ba6b14c73706835fe679b.png

这个超强了,每秒可以生成百万级别的id(加熵之后,使用的算法不一样了,总之加熵时uniqid更快了),重复率为0

多进程版执行结果

ab4bddddbd9752786c63092d2d378a04.png

重复率为0,可以断定一该方式生成唯一id可行,性能和重复概率表现很好,唯一不足就是id无序

2. 使用session_create_id()

该函数是php7.1之后提供的,是php用来生成session_id使用的,php使用它来生成每个请求会话,应该唯一是相当好的,不然呢!测试一波:

$random = session_create_id(); //长这样子:6khfg75a13khre330nqu1t84ab

单进程版执行结果

cfa2e5026173c4f4034f4c151449b38d.png

多进程版执行结果

73d66e29dddb76d1da964d975db561a6.png

由上可见,session_create_id性能和重复率都表现不错,值得信赖

3. 使用uuid

uuid已经形成的国际规范,目前出来的版本有四五个,最常用的是由时间戳+顺序号+机器标识+进程标识规则生成的id,与此相关的composer包很多了,不造重车轮是最大的生产力,直接选用 https://packagist.org/packages/ramsey/uuid 包,测验一下:

$random = Uuid::uuid1()->toString(); //长这样子:480dac52-3102-11ea-89e3-525400cae48b

单进程版执行结果

e94b6f0209ff684eaedd88a0c470a535.png

多进程版执行结果

dad8d58c12f7f2d6fc94a5177d2e89fa.png

结果令人意外,uuid生成速度还行,但是多进程异步生成时,并不能保证高标准的唯一性,出现了8个重复的,虽然很少,但是很意外,说好的全球唯一呢,有点虚,哈哈!总的来说还是不错的,重复率已经做到相当低了。

4. 使用雪花算法

以上三种都是只会生成无序的随机字符,但是我们有些时候需要依靠唯一id对数据库进行排序的,比如我们要生成全库唯一id时,我们就需要id满足顺序性、整数类型(提高索引效率),而由雪花算法生成的id可以满足这一点(雪花id有时间顺序)。雪花算法原理图如下:

5f0149e19561ef3e8fb74a89c9133026.png

$snowflake=new Snowflake();

//可以添加机器码,可设置1024个,以增强随机性,但1024太小治标不治本,当集群进程数超于这个时,加上多机器时间戳的不靠谱了,还是会出现问题,这里只做简单使用

$random = $snowflake->id(); //长这样子:55141599398592512

单进程版执行结果

9b7cae11b42fbf0261f89b4101dcd80a.png

多进程版执行结果

93aad70fa1dce173394101317409fb00.png

分析结果,单进程使用雪花算法生成id,可以保持十几万个每秒的生成速度,且重复率保持为0,但是去到并发生成时,问题出现了,重复了很多,重复率1/2,恐怖,可断,雪花算法如上面那样简单使用的话,是难以适合高并发场景的!

5.id计算器生成id

id计算器生成id就是的首要工作就是要设置一个公共变量,该变量增量为1,每次进程从这里申请一次id,id的值都会加一,这样一直累加下去,就保证了全局唯一性,且都是整数。这方面,Redis很能胜任,一是性能很好,二是跨机器,另外Redis提供的原子性函数incr,简直天造地设了。来,操作一波:

$random = Cache::inc('id'); //长这样子:1,2,3,4,5,...

单进程版执行结果

aafd6d664f7bd01129b2675a136f45db.png

多进程版执行结果

d37ce5a48ca910da6a2f53c43c39f8b0.png

这种方式生成的id,能够保持很好的id递增性,即保证的顺序性,由于incr是原子操作,重复率几乎为0,产生速率,有赖于Redis的软件性能、网络等等因素,也算是了比较好方案了。

总结

以上各种id生成方式,各有各的优缺点,不能一概而论。要哪一种更好,还是要看具体需求,适合就是最好的。但总的来说,唯一性、性能是绝对高尚的。

除了上述几种方式之外,还有很多生成唯一id的方案,也可以奇技淫巧一下(猥琐发育),在上面几种方式为基础打造新的更加高效和可靠的生成方式

原创不易,分享快乐,渴望动力

本作品采用《CC 协议》,转载必须注明作者和本文链接

我只想看看蓝天

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值