求 16 位纯数字订单号产生算法

2020-12-26 16:35:25 +08:00
 Aluhao
最近业务方要求订单号不能大于 16 位,以前写的 18 位算法不能用了,改成 16 位重复的概率太高了,问问大家有什么好的算法分享!

现在用的 18 位:

<?php declare(strict_types=1);
date_default_timezone_set('Etc/GMT-8');
$time = microtime(true);
function order()
{
$ids = 0;
$aes = substr(microtime(), 2, 6);
$pid = $aes . getmypid() . mt_rand(0, 999999);
for($in = 0; $in < 18; $in++) {
$ids += (int) (substr($pid, $in, 1));
}
return time() . $aes . str_pad(strval($ids), 2, '0', STR_PAD_LEFT);
}

$arr = array();
for($i = 0; $i < 1000000; $i++) {
$arr[] = order();
}
$arrs = array_count_values($arr);
echo 'COUNT ' . count($arrs);
echo '<br>';
echo 'RAND ' . array_rand($arrs);
echo '<br>';
arsort($arrs);
$as = [];
foreach($arrs as $ids => $aos) {
if ($aos > 1) {
$as[] = $ids;
echo $ids . ' - ' . $aos . '<br>';
}
}
echo 'COUNT ' . count($as);
echo '<br>';
echo 'Time ' . number_format((microtime(true) - $time), 3);
6719 次点击
所在节点    PHP
43 条回复
hbolive
2020-12-26 16:51:54 +08:00
单纯依靠算法,16 还是 18 位,都没法保证吧重复吧。
所以应该有其他机制校验下生成的 ID 是否重复。
laminux29
2020-12-26 16:55:08 +08:00
为啥订单号就不能老老实实从 1 开始自增?为啥订单号就不能常见的无符号 32 位整型?
Aluhao
2020-12-26 16:56:26 +08:00
@hbolive 是有校验,产生的订单写入 Redis 了,产生的时候从 Redis 检查一次。
pxw2002
2020-12-26 17:02:13 +08:00
有几位根据日期时间生成
就不会重复了吧
bleutee
2020-12-26 17:03:29 +08:00
@laminux29 萬一人家想操數據呢?一五一十的順序排列,咋操
Aluhao
2020-12-26 17:03:53 +08:00
@pxw2002 用毫秒加随机 2 位,每秒并发 1 万容易重复的。
Aluhao
2020-12-26 17:06:19 +08:00
@laminux29 自增要先写数据库,很多多业务是先拿订单号请求接口成功后才写入数据库。
还有就是自增从 1 开始,容易被人查数据。
jimmyismagic
2020-12-26 17:11:59 +08:00
业务方的订单号和你内部的订单号完全可以分开啊,重复就重复呗
Aluhao
2020-12-26 17:14:48 +08:00
@jimmyismagic 这个就是内部订单号,不能重复的。
jimmyismagic
2020-12-26 17:22:04 +08:00
@Aluhao 所以他到底是甲方还是乙方?乙方糊弄过去就行了,甲方就怼回去,不懂瞎指导
bsg1992
2020-12-26 17:24:44 +08:00
时间戳 不就解决了吗
crclz
2020-12-26 17:28:38 +08:00
你说的 16 位是“个十百千万”的位吧,不是 bit 吧。

如果是 16 个字符,那么约束我猜就是前端 js 的 number 超过 53~54bit 就会损失精度吧。

一个解决方案是传字符串。

如果非要数字,也不是不可以。你去看看 snowflake 算法。https://github.com/beyondfengyu/SnowFlake/blob/master/SnowFlake.java 把第 18 行里面的几个东西改一下,就可以应对不同的位数,本质是(使用年限,机器数量,数据中心数量,每秒并发数量)的权衡。当然首先得去看看 showflake 的结构再来改。
Jooooooooo
2020-12-26 17:29:29 +08:00
用时间戳+机器码

每个机器上维护一个当前时间戳可用的序号

你并发不至于那么大
Aluhao
2020-12-26 17:30:08 +08:00
@jimmyismagic 要拿内部唯一订单号去关联对方订单信息,要先请求传 16 位订单号过去。
xuanbg
2020-12-26 17:31:15 +08:00
魔改雪花算法,41 位时间戳不要动,10 位设备去掉,差不多就是 16 位数字了。

但是,我估计你要的是 yyyyMMdd+2 位业务代码+2 位 xx+4 位流水号,流水号还要加密的那种。在我的 github 里面有,请自取
DarkCat123
2020-12-26 17:31:32 +08:00
@laminux29 容易被人遍历数据,分析…… 而且不利于分布式数据库的 hash 分区。

原来的 18 位用的是 snowflake?
我建议要不要魔改下 snowflake, 牺牲一下时间来换位数降低(我记得原来的 snowflake 可以用 69 年)。
要不然考虑找个 md5 sha1 之类的 hash 之后转 hex 看看能不能到 16 位吧。
Aluhao
2020-12-26 17:31:56 +08:00
@Jooooooooo 主要是只限制 16 个数字,长一点都好解决。
DarkCat123
2020-12-26 17:32:06 +08:00
@xuanbg 老哥好巧,我们同时提到了魔改 snowlflake……
jimmyismagic
2020-12-26 17:34:53 +08:00
@Aluhao 那你从 1 开始不就行了,对方唯一,需要你传的唯一,对方根本不关心你传的什么,楼上的 snowfake 一般用的很多,分布式 id 用的比较多,单机也没什么问题,16 位 50 多个比特完全够了
dorothyREN
2020-12-26 17:43:05 +08:00
13 位时间戳 加 3 位随机数或者 3 位自增数字

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/739204

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX