如何才能生成一个唯一的随机数

2012-08-27 18:14:26 +08:00
 macdino
正在做一个优惠券相关的系统:
要求:生成的优惠券验证码为6位,并且唯一。
因为系统中有N个优惠券同时提供,所以这个6位不好生成唯一。
大家有什么好的想法或算法。
系统的服务并发不大,预计每天请求20W次左右。
12079 次点击
所在节点    问与答
41 条回复
ljbha007
2012-08-28 01:30:48 +08:00
可以参考mongodb的ObjectID生成方式
http://www.mongodb.org/display/DOCS/Object+IDs

比如你的例子可以前两位为服务器的ID 中间两位为服务进程的ID 最后两位为该进程的计数器的ID
每一位是36进制的(0~9A~Z)
ljbha007
2012-08-28 01:32:41 +08:00
如果只有一个服务器或者只有一个进程的话还可以把其中一个改为当前时间戳
no2x
2012-08-28 01:42:04 +08:00
6 位验证码假设用 10 个数字 + 26 个大写字母,那么就是 36 ^ 6 的容量

可以用(自动增长的 id 序 + 带微秒的UNIX时间戳)来得到这个 21E 以内的 10 位有效数字,然后转成验证码。

id 序 和 时间戳 都在变化,不会产生重复问题,而且无规律可循。
qiuai
2012-08-28 08:49:19 +08:00
我记得MD5不是有16位的么?把UUID加密称16位的MD5好了.
ipconfiger
2012-08-28 09:26:54 +08:00
最简单的办法就是预先生成 000000 ~ 999999 的全部组合,不过100W条而已,内存里随便存。然后只需要随机索引去取就好了,取完就remove掉,只要保证取-remove这个过程的原子性,那么取是随机的,然后不重复。
macdino
2012-08-28 09:42:25 +08:00
@013231 @ipconfiger 这样子是最好的方法,一个队列去处理。但是我的优惠券很多很多,如果说一个优惠券占用1M,哪么N张的话,这个量也不少。。。如果用库的话,就得考虑锁的事。如果用队列,就得考虑维护队列的成本。
ipconfiger
2012-08-28 09:48:04 +08:00
@macdino 可以分段生成、分批发放,比如一批次生成10W个,用得差不多的时候再生成5W个放进池里待用,差不多了再来5W个,这样内存消耗的最大值不会持续递增,总体来说仍然是随机的,而且也绝不会重复
macdino
2012-08-28 09:48:30 +08:00
@acalarolo 多谢。因为这个值在生成之前和用户无关,而且允许一个用户多次申请。所以拿用户的信息来参与计算,重复机率有点大;
@qiuai @no2x 俺得需要6位数字的。。
@gockxml 多谢。我研究一下。
Numbcoder
2012-08-28 10:02:08 +08:00
自增id序列 + 时间戳 ,然后转化为36进制
qiuai
2012-08-28 11:15:17 +08:00
@macdino =.=我看错了...6位的话.为什么一定要随机数,而不能6位按顺序排列呢...
ljbha007
2012-08-28 11:22:56 +08:00
@ipconfiger remove不但要原子性 还要保证不能和读取产生竟态条件
skywinger
2012-08-28 11:40:53 +08:00
@macdino 这个简单啊,用时间(GMT)做一下SHA-1运算截取前6个字符即可。
skydiver
2012-08-28 13:06:10 +08:00
@qiuai 按顺序的话,优惠码就可以猜出来了啊。。。
no2x
2012-08-28 13:52:23 +08:00
@macdino 每天 20w 的读取,只准 6 位数字?那不是 2 、3 天就不够用了?
macdino
2012-08-28 14:49:08 +08:00
@no2x 读了,用了,可以再次生成了就。
soulhacker
2012-08-28 15:26:30 +08:00
几次想回这个但是都不知道咋回。。。楼主有些约束没有说清楚,比如:

1. 生成号码的N个「东西」到底是啥?是进程?线程?还是分布式的节点?
2. 生成的频率和响应时间要求如何?最重要的是,需要实时生成吗?为什么?
3. 6位只能是数字还是可以是字母?
macdino
2012-08-28 20:24:56 +08:00
@soulhacker 可能是我有些没有说清楚;

1、我是针对不同的优惠券发放不同的优惠券码,系统中可能同时有很多优惠券在发放,最后是认码的,所以这些码是唯一的。
2、不一定实时生成。生成的频率系统设计,每秒请求5个码,响应时间小于150ms(期望值 )
3、6位string型的,但是每一位都是数字。因为会出现012345这种码。
no2x
2012-08-28 22:23:37 +08:00
@macdino 那不是新旧冲突?汗。。我不明白你这个验证码究竟什么使用规则了。
macdino
2012-08-29 09:40:54 +08:00
@no2x 不会冲突啊。用过了,我就可以再申请下来的啊。
soulhacker
2012-08-29 10:11:51 +08:00
@macdino 如果没有很强的实时性、分布式考虑,最简单有效的办法是把所有号码生成好,然后使用一个乱序算法打乱次序(就是一个数据表嘛,撑死100万条记录),然后使用一个号码生成服务来把所有请求串行化(这个逻辑非常简单,可以做到单机每秒上千次请求处理没什么压力),申请一个给一个。

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

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

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

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

© 2021 V2EX