请教各位 V 友一个神奇的夺宝算法(二)

2016-07-05 04:25:25 +08:00
 adkudao
业务描述见上一个帖子:
http://v2ex.com/t/287935

在众位 V 友的建议下, 我采用 Redis+MySQL 结合的思路:
事先将号码批量生成, 保存在 redis 的 list 中, 夺宝时就 spop 一个, 存入 MySQL

但构想很美好, 现实很残酷, 我用 phpredis (PHP 扩展,性能最优的方案)来批量生成号码时, 一但号码数量上了 45W, 整个操作就失败了,而且就算只生成 40W 号码, 也需要七八秒的时间, 这跟我预想的完全不一样;

我的代码如下:
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
$redis = new Redis();
$redis -> connect('127.0.0.1', 6379);

$haomas = range(1, 480000);
$redis -> delete('test');
$sTime = microtime(true);
$redis -> multi(Redis::PIPELINE);

foreach ($haomas as $key => $value)
{
$redis -> rpush('test', $value);
}

$redis -> exec();
$eTime = microtime(true);

var_dump( ($eTime - $sTime)*1000 );
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---


之前有位 V 友 @lecher 提到过另一种思路, 通过自增 ID 来实现伪随机 id*num1+num2 , 可是光看" id*num1+num2 "这么一串代码 , 我也不知道该如何入手才好, 不知道这个算法有没有具体的案例教程?


或者说集思广益, 请教众位 V 友, 要解决这种生成几百万夺宝号码的需求, 还有更优更聪明的算法吗?
4512 次点击
所在节点    问与答
60 条回复
zingl
2016-07-05 15:36:52 +08:00
在随机性可保证的前提下,只需要保证拿号不重复即可,不需要生成随机号,从连续号池里顺序或者随机取都可以
adkudao
2016-07-05 15:48:55 +08:00
@liubo
真好玩, 直接如下是可以的
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
$redis -> rpush('test', '1', '2', '3');
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---



但是这样就不行:
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
$haomas = range(1, 3);

foreach ($haomas as $key => $value)
{
$str .= ','. $value;
}

$redis -> rpush('test', substr($str, 1));
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---



然后这样也不行:
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
$haomas = range(1, 3);
$redis -> rpush('test', $haomas);
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---



天呐, PHP 该怎样批量传递参数啊
adkudao
2016-07-05 15:51:38 +08:00
@lijinma
这个对于用户来讲就完全不可行的, 因为等于是后台可以随机指定一个数字作为幸运号码
也就是说, 后台想让谁中奖谁就中奖, 那这个产品就很难推广了
500miles
2016-07-05 15:57:15 +08:00
@adkudao

使用 pipeline, 可以提高插入性能
adkudao
2016-07-05 15:59:03 +08:00
@500miles
这个测试结果就是用了以后的结果, 不用 pipeline 性能更糟
cevincheung
2016-07-05 15:59:11 +08:00
有用 set 集合。还能实现随机取出。
有批量插入,一般几十万在 0.xs 之内。
500miles
2016-07-05 16:09:35 +08:00
@adkudao 为什么要事先生成呢?

夺宝者购买时, 再临时生成号码, push 进队列满足不了需求么?
adkudao
2016-07-05 16:25:02 +08:00
@cevincheung
有 php 的代码参考吗?
adkudao
2016-07-05 16:27:19 +08:00
@500miles
如果是小件商品, 用户买 1 个,就临时生成一个, 买 100 个, 就临时生成 100 个, 可能问题不大
主要是大宗商品, 比如房车之类的, 用户会不会一次买个几十万难说, 但客户测试的时候,我估计必然会直接买个几十上百万的, 如果临时生成, 估计要等好几分钟, 这样体验不好
cevincheung
2016-07-05 16:30:45 +08:00
3dwelcome
2016-07-05 16:47:12 +08:00
就是 GUID 的数字化表示吧,只要保证不重复就可以了。

连续生成 1 百万个 GUID, 问题不大的。
lijinma
2016-07-05 16:52:10 +08:00
@adkudao

我的方法的关键要靠你自己控制:最后一步必须要调用 https://www.random.org/ 来获取随机数。

按照你的方法,任何人都看不到 幸运号码是多少,你后台仍然可以随时修改整个幸运号码,不是吗?
morethansean
2016-07-05 16:57:46 +08:00
是说不相信随机数生成函数吗?
为什么不能从 0 到 n 分配?
3dwelcome
2016-07-05 16:59:36 +08:00
可以考虑用当天的双色球开奖号码作为随机数,取个余数就可以了,对用户透明,也公证无比。
11138
2016-07-05 17:14:02 +08:00
用 perl 的 C 扩展生成 100 万大概 2.5 秒。
用 PHP5.6.2 生成 100 万大概 4 秒。
Martin9
2016-07-05 17:14:49 +08:00
@3dwelcome
虽然很有道理,但感觉有点喜感。
(中奖的人一定后悔没买双色球
11138
2016-07-05 17:15:54 +08:00
@adkudao “一但号码数量上了 45W, 整个操作就失败了”有什么错误信息么?
默认的 PHP 有内存限制,我加了
ini_set ('memory_limit', '300M');
才成功。
11138
2016-07-05 17:24:59 +08:00
@adkudao

$a = array();
array_push($a,'1','2','3');
$redis -> rpush('test', $a);
11138
2016-07-05 17:31:02 +08:00
<?php
ini_set ('memory_limit', '500M');
$redis = new Redis();
$redis -> connect('127.0.0.1', 6379);
$haomas = range(1, 1000000);
$a=array();
foreach ($haomas as $key => $value)
{
array_push($a,$value);
}
$redis -> delete('test1y');
$sTime = microtime(true);
$redis -> multi(Redis::PIPELINE);
$redis -> rpush('test', $a);
$redis -> exec();
$eTime = microtime(true);
var_dump( ($eTime - $sTime)*1000 );
?>

这样生成 100 万大概半秒钟。
11138
2016-07-05 17:31:53 +08:00
test1y 改为 test

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

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

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

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

© 2021 V2EX