PHP性能优化探索

如题,在开发过程中总会遇到的一些性能优化上的疑惑点,这里我整理一下以便于后面自己的复习吧。

排序问题

场景如下:
有时候,根据业务场景的不同,总会遇到一些比较容易出分歧的思路。比如我今天遇到了一个“直播话题”相关的需求,后台接口返回的是近期正在使用的直播话题,同时后台可以通过对其进行上移下移实现APP中固定顺序的输出。
老代码中是这样做的,用redis的一个sortedset类型的key记录着所有直播话题,当前时间戳作为话题(关联数组被json_encode后的member)的值。然后每次拿出所有的member,遍历每一个member(json_decode后有starttime和endtime字段)来过滤出当前有效的话题详情,最后通过PHP的ksort方法实现整体的排序。
看到这里,我就有点怀疑这段代码的性能了,火星每天开播的主播不在少数,而且这个接口在不同的地方都有可能被调用,可想而知,QPS也是一个不小的考验。那么有没有什么更快的实现呢?比如从设计key的时候,话题ID作为sortedset的topicid和score,而每一个topicid作为一个hash的member,对应话题详情(json_encode后的值)作为这个member的值。遍历的时候只需要找出endtime大于当前时间的topicid,然后再获取对应的话题详情。
具体哪一种我也不是很清楚,毕竟业务逻辑在那摆着呢,下面针对这个问题,来做一个试验好了。

老代码

<?php
$starttime = microtime(true);
$redis = new Redis();
$redis->connect("ip", 6379);
$redis->auth("auth");
$key = "questions:sort:old";
//$redis->del($key);
/* 
//模拟存储源数据
for($index=0; $index< 1000; $index++) {
    $topicid = time() + rand(-500, 500);
    $details = array("starttime"=>$topicid, "endtime"=>intval($topicid), "desc"=>"直播话题详情:{$topicid}");
    $redis->zadd($key, $topicid, json_encode($details));
}
 */
echo time()."\n";
// 根据老代码逻辑,拿到结果
$topics = $redis->zrevrange($key, 0, -1, true);
$result = array();
foreach($topics as $member=>$score) {
    $member = json_decode($member, true);
    if($member['endtime'] > 1525962300) { // 固定下时间戳
        $result[$score] = $member;
    }
}
ksort($result);
var_dump("共获取:".count($result)."个直播话题!\n");
echo "共计耗时:".(microtime(true) - $starttime)."毫秒\n";

执行结果如下:

  • member数量为1000的时候
root@aliyun:/var/www/html/questions/sort# php old.php 
1525963267
string(32) "共获取:329个直播话题!
"
共计耗时:0.021611928939819毫秒
  • member数量为100000的时候:
1525963486
string(32) "共获取:489个直播话题!
"
共计耗时:6.1188609600067毫秒

新方法

<?php
$starttime = microtime(true);
$redis = new Redis();
$redis->connect("ip", 6379);
$redis->auth("auth");
$sortkey = "questions:sort:new:sort";// sortedset
$storekey = "questions:sort:new:store";//hash
//$redis->multi()->del($sortkey)->del($storekey)->exec();
/* 
//模拟存储源数据
for($index=0; $index< 1000; $index++) {
    $topicid = time() + rand(-500, 500);
    $details = array("starttime"=>$topicid, "endtime"=>intval($topicid), "desc"=>"直播话题详情:{$topicid}");
    $redis->multi()->zadd($sortkey, $topicid, $topicid)->hset($storekey, $topicid, json_encode($details))->exec();
}
 */
echo time()."\n";
// 根据老代码逻辑,拿到结果
$topicids = $redis->zrevrange($sortkey, 0, -1, true);
$result = array();
foreach($topicids as $topicid) {
    if($topicid > 1525962703) { // 固定下时间戳
        $result[$topicid] = json_decode($redis->hget($storekey, $topicid));
    }
}
//ksort($result);
var_dump("共获取:".count($result)."个直播话题!\n");
$endtime = microtime(true);
echo "[{$starttime}], [{$endtime}]\n";
echo "共计耗时:".($endtime-$starttime)."毫秒\n";

执行结果如下:

  • member 为1000的时候:
1525963345
string(32) "共获取:292个直播话题!
"
[1525963345.6308], [1525963346.2325]
共计耗时:0.60166215896606毫秒
  • member为100000的时候:
1525963574
string(32) "共获取:484个直播话题!
"
[1525963574.1738], [1525963577.0668]
共计耗时:2.8929860591888毫秒

实验分析

从上面的数据可以发现这样的一个现象:

  • 当member也就是话题数量较少的时候,选择老代码的方式速度更快。
  • 当member数量较多的时候,选择新代码的方式速度会更快。

其实,仅仅有上面的测试样例,是不充分的。除了对速度的测试,我们还要考虑到redis服务器的性能,redis的QPS以及单次请求的数据压力(比如,返回值大小超过XX的时候,Redis就不能正常工作了,具体的数组也和配置,也和服务器本身的硬件性能有关,这里不做过多考究)。Redis本身对于member的长度等都是需要考虑的。但更重要的是,根据业务需求来选择合适的方案,这里直播话题通常来说根本不会达到的级别,所以老代码的方式更为妥当,但是这不是说所有的场景都适合用老代码的方式构建,具体的场景,具体的业务需求,都是需要首先考虑在内的,功能做不出来,何谈优化呢。


未完,待续…

猜你喜欢

转载自blog.csdn.net/marksinoberg/article/details/80275008