请教各位 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 友, 要解决这种生成几百万夺宝号码的需求, 还有更优更聪明的算法吗?
4488 次点击
所在节点    问与答
60 条回复
adkudao
2016-07-05 18:00:12 +08:00
@11138
屌炸天!!! 性能比 call_user_func_array 快了 6 倍!
adkudao
2016-07-05 18:08:52 +08:00
@11138

贴一段之前的方案, 要十几秒不止, 你这个方案简直炸了! 不是提升 6 倍, 是提升了十几二十倍! 你牛逼!!

$redis = new Redis();
$redis -> connect('127.0.0.1', 6379);

$arrayName = array('test');
// $redis -> delete('test');
$sTime = microtime(true);
// $redis -> multi(Redis::PIPELINE);

$haomas = range(1, 1000000);

foreach ($haomas as $key => $value)
{
array_push( $arrayName , $value );
}

call_user_func_array([$redis, 'rpush'], $arrayName);

$eTime = microtime(true);

var_dump( ($eTime - $sTime)*1000 );
adkudao
2016-07-05 18:13:44 +08:00
@Martin9 @3dwelcome
哈哈, 两位可以考虑兼职相声演员, 俺们 V 站的朋友一定给你们捧场
adkudao
2016-07-05 18:41:54 +08:00
@11138
兄弟, 好像闹了个乌龙, 你这个代码插进去直接变为了 test => 'Array', 所以速度那么快....
11138
2016-07-05 19:04:30 +08:00
@adkudao 抱歉,刚才赶着出门,还想着回头再测试各种情况。
PHP 用 call_user_func_array 来处理 100 万记录我测试这大概 0.7 秒。等下再测试其它集合的速度。
E5-2680 v3 @ 2.50GHz
jhdxr
2016-07-05 19:13:16 +08:00
$redis -> rpush('test', $a);
应该是
$redis -> rpush('test', ...$a);

另外
array_push( $arrayName , $value );
建议直接改为
$arrayName[] = $value;

这种都是手册上有的内容,有问题不能先看看手册么
3dwelcome
2016-07-05 19:15:30 +08:00
楼主貌似没理解我的意思。夺宝这种投注类游戏、最重要的是公信度透明化、一旦有暗盒操作嫌疑、以后就再也没人气了。

比如一百人夺宝、要开奖的时候、你去查一下上证指数、取小数点最后两位、作为获奖者随机 id 、这样后台也不可能作假、很有公信度。
jhdxr
2016-07-05 19:15:43 +08:00
call_user_func_array 与直接调用函数相比肯定是后者更快。你顶楼所提到的方案之所以慢的无法忍受是因为循环中有 IO 操作
11138
2016-07-05 19:58:02 +08:00
call_user_func_array([$redis, 'rpush'], $a);
改为
$redis -> rpush('test', ...$a);
这样快了 0.1~0.2 秒。

array_push($a,$value);
改为
$a[] = $value;
速度没变化。
adkudao
2016-07-05 20:20:10 +08:00
@3dwelcome 这样其实也是好的, 而且具有实时性, 不像时时彩还有延迟, 不过客户不同意
adkudao
2016-07-05 20:25:51 +08:00
@jhdxr 嗯,谢谢建议, 已改进
adkudao
2016-07-05 20:30:32 +08:00
@11138
兄弟, 用你之前的 rpush 方法, 插入 list 不成功, 还是用的 call_user_func_array, 时间大概在 3 秒左右,
代码如下:
--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
$sTime = microtime(true);

ini_set ('memory_limit', '800M');
$redis = new Redis();
$redis -> connect('127.0.0.1', 6379);

$arrayName = array('test');
$redis -> delete('test');
$redis -> multi(Redis::PIPELINE);

$haomas = range(1, 1000000);

foreach ($haomas as $key => $value)
{
array_push( $arrayName , $value );
}

$f = call_user_func_array([$redis, 'rpush'], $arrayName);

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

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

你之前的 rpush 方法, 还是可以成功吗?
just4test
2016-07-05 21:04:50 +08:00
@adkudao 客户对幸运号码的生成方式有什么要求?
adkudao
2016-07-05 21:09:11 +08:00
@just4test
1. 一个 10 块钱的商品, 分别对应 10 个号码
2. 用户花一块钱, 可以随机抽取一个号码
3. 10 个号码抽完后, 得出一个幸运号码, 谁拥有这个号码, 谁就可以获得商品

重点不是幸运号码如何得出, 重点是用户能 "随机" 抽取幸运号码

随机抽取几千个号码不是事, 重点是当一个商品, 比如一套房子, 往往有上百万个号码, 这时要供用户随机抽取, 就比较考验算法和架构了

目前用 call_user_func_array 来向 redis 中生成百万个号码, 大概用时三秒左右, 是目前已验证的最有效率的, 不知道网易他们是怎么解决的

感觉他们的更快
11138
2016-07-05 21:42:23 +08:00
@
$f = call_user_func_array([$redis, 'rpush'], $arrayName);
改为
$redis -> rpush('test', ...$arrayName);


<?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)
{
$a[] = $value;
}
$redis -> delete('test');
$sTime = microtime(true);
$redis -> multi(Redis::PIPELINE);
$redis -> rpush('test', ...$a);
$redis -> exec();
$eTime = microtime(true);
var_dump( ($eTime - $sTime)*1000 );
?>

刚才同时测试了 Set 集合,比 List 列表慢一秒。
11138
2016-07-05 21:43:44 +08:00
@adkudao 快慢这也跟 CPU 有关啊。
adkudao
2016-07-05 22:00:42 +08:00
@11138
我刚试过了, 完全可以了, 我这里大概一秒多, 这应该是 Redis 批量生成号码, 最快的形式了,非常感谢
fuxkcsdn
2016-07-05 22:00:58 +08:00
fuxkcsdn
2016-07-05 22:05:14 +08:00
adkudao
2016-07-05 22:18:41 +08:00
@fuxkcsdn
哈哈, 手疾眼快, 好习惯

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

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

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

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

© 2021 V2EX