生成字母 + 数字的六位数唯一码

有个优惠码的需求,由于历史原因,优惠码需要是由小写字母跟阿拉伯数字组成的六位数的码,并且必须是唯一的.

找不到什么高效的办法,也看不懂高大上的算法.. 那就土办法吧.

优惠码的形式是六位数的 36 进制数,最大值为 zzzzzz 也就是 十进制的 2176782335, 也就是最多会有 2176782336 个码。随机生成肯定是不靠谱的,从 0 开始自增也不靠谱,那样容易被猜到其他的优惠码.

那么换成,把 2176782336 个码按照递增顺序均匀切割成若干块,每次随机取一块,然后从块里面按自增获取一个码就好了.

假设每块里面有 50000 个码,切割后会有 43536 块,把每块里面开始的码当作种子 (a) 存入种子池,记录每块里面已获取的码的数量 (b), 每次获取后,把 b 自增.

那么当获取到种子后,计算码的公式为:

a * 50000 + b

然后再把获取到的码转换成 36 进制.

实现上,可以把使用 redis 存放种子池,使用一个有序集合,member 为种子值,score 为已获取的码数量.

一个简单的实现:

代码已被折叠,点此展开

写了一个小脚本,死循环生成 code, 把 code 放到一个无序集合里面,利用 sadd 的返回值判断是否有重复,生成了七千多万个码都不会没有重复。棒棒哒

GitHub: https://github.com/RunnerLee/idiot-uuid

本帖已被设为精华帖!
本帖由系统于 7年前 自动加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 6

另一个解决方案:自增 id 然后 hashid 。 自增 id 是可以保证唯一,然后用 Hashids 加密,Hashids 设置字母表为 0-9a-z

ID 混淆扩展包 Laravel Hashid:分享:ID 混淆扩展包 Laravel Hashid 新鲜出炉

示例:

$ composer require elfsundae/laravel-hashid

Laravel 5.5 的话会自动注册 ServiceProvider,直接用 tinker 测试:

$ php artisan tinker
Psy Shell v0.8.15 (PHP 7.1.11 — cli) by Justin Hileman
>>> config([
... 'hashid.connections.hashids_integer.alphabet' => 'abcdefghijklmnopqrstuvwxyz1234567890',
... 'hashid.connections.hashids_integer.min_length' => 6,
... ])
=> null
>>> hashid_encode(1)
=> "jdkoe5"
>>> hashid_encode(100000)
=> "ejpzky"
>>> hashid_encode(999888) 
=> "3rp2jp"
>>> 
7年前 评论

@ElfSundae 赞。原来有看到这个 hashids, 没注意到是支持加 salt 和自定义字典的.

但是也有不足的地方,我尝试了一下,用 uniqid() 生成一个随机的 salt, 然后 alphabet 设置为 abcdefghijklmnopqrstuvwxyz1234567890, minHashLength 设置为 6.

将自增 ID 进行 encode, 简单试了一下,在 7962624 的时候,返回的码开始增加到了 7 位。也就是如果要求优惠码只能 6 位的时候,用上面的配置,只能生成接近 800w 个码.

不过 800w 也够我们这破公司用很多了 hhh.

7年前 评论

@RunnerLee 嗯,限定优惠码长度就限定了优惠码数量... 如果在这个糟糕的、致命的、无意义的前提下还要能生成最大数量的唯一码,那只能自己实现发码了... 现成的、成熟的、高性能的算法可能不合适

7年前 评论

@ElfSundae 是的。不过也不是在说一定要有这么苛刻的条件,提前预知可能的问题而已。毕竟.. 万一这套代码长命到已经把码全部发完了呢 :smile:

7年前 评论