最近在做一个游戏数据统计后台,最基础的功能是通过分析注册登录日志来展示用户数据。在公司内部测试,用户量很少,所以就没有发现什么性能问题。但是这两天一起放到真实的测试环境,用户量噌噌地就涌进来了,从下午开始,在线人数的统计开始卡,几秒钟才返回数据;注册人数的查询速度还行。到了晚上,在线人数的统计基本上就加载超时打不开了。虽然不知他们游戏端那边什么BUG,玩家那边登录经常出问题,导致在线人数和注册人数并不是很多。但是就这一点数据量我这边查询的速度也不行,这就很尴尬了。
现在他们那边在查游戏的BUG,我这边也在看统计后台的代码到底性能出在哪里。首先说明一下,我统计用的数据是从库,他们游戏用的是主库,再说我这边管理员人数就几个,不可能会影响到游戏服的性能问题。
今天项目组长把数据库都导过来到公司内的服务器。我拷了一份到本机,看看统计平台的性能问题出在哪里。然后却发现,居然连注册统计都非常卡,服务器上是两秒左右返回,本机要二十几秒,还经常超时(PHP的默认配置是30秒超时);在线统计的就不用说了肯定打不开。看了一下数据库,当天的注册记录也就 3500 条左右(有假数据),每五分钟统计一次,一天就是统计 288 次。当然这里肯定不是循环查询数据库288次,那样会被骂死的吧。
统计时间段内的注册数,逻辑也非常简单,就是每个时间段遍历一次数据,比较时间大小,符合就+1。但是为什么这么简单的逻辑,也就一百万次循环,怎么会跑出了足足半分钟的时间那么久呢?
关键问题就出在于 时间比较这里了,我们都知道,时间戳是比较时间大小的一个比较科学的方法,而数据库里记录的时间一般都是以 YYYY-mm-dd HH:ii:ss 的形式,PHP里有strtotime的函数转换成时间戳。然而在288个for * 3500个foreach 的加持之后,这里的执行时间长达半分钟。
$nowDayDT = strtotime( date('Y-m-d') ); $__startT = microtime(TRUE); for($i=0; $i<$allTime; $i += $gapTime){ $count = 0; //用于数据比较的 $startDT = $nowDayDT+$i; $endDT = $nowDayDT+$i+$gapTime; //用于显示的 $xAxis1 = date('H:i', $nowDayDT+$i); $xAxis2 = date('H:i', $nowDayDT+$i+$gapTime); foreach($rawData as $line){ $time = strtotime($line['log_dt']); if( $startDT<=$time && $time<$endDT ){ $count ++; } } $resArr[] = [ 'date'=>$xAxis1.'~'.$xAxis2, 'number'=>$count ]; } echo microtime(TRUE)-$__startT;
那这样的话,基本上是没办法再用这个strtotime的函数的了,那还有什么办法比较时间大小呢?答案很简单粗暴,PHP里面可以直接比较两个日期时间字符串!所以改过后的代码如下。然后现在的运行时间大概是 0.3秒
$__startT = microtime(TRUE); for($i=0; $i<$allTime; $i += $gapTime){ $count = 0; //用于数据比较的 $startDT = date('Y-m-d H:i:s', $nowDayDT+$i); $endDT = date('Y-m-d H:i:s', $nowDayDT+$i+$gapTime); //用于显示的 $xAxis1 = date('H:i', $nowDayDT+$i); $xAxis2 = date('H:i', $nowDayDT+$i+$gapTime); foreach($rawData as $line){ $time = $line['log_dt']; if( $startDT<=$time && $time<$endDT ){ $count ++; } } $resArr[] = [ 'date'=>$xAxis1.'~'.$xAxis2, 'number'=>$count ]; } echo microtime(TRUE)-$__startT;
遍历再优化
大家可能发现一个问题,for 里面嵌套一个 foreach,这性能有点担忧,其中里面的 foreach 有必要完全遍历吗?其实是不必的。只要查SQL数据的时候,按时间排序排出来。优化后的时间比较算法如下:
for{ ... foreach($rawData as $line){ $time = $line['log_dt'];//strtotime($line['log_dt']); //优化算法计算 if($time<$startDT) continue; //小于开始时间则跳过 if($time>=$endDT) break; //大于结束时间则结束 $count ++; //否则为符合条件 //原始的算法 // if( $startDT<=$time && $time<$endDT ){ // $count ++; // } } ...}
这里巧用了 continue 和 break 关键字,用于跳过一次循环和结束整个循环。这次的话,一天中刚开始的时间统计中,后面很大一部分数据的都可以直接跳过。最后总遍历时间缩短为约0.12秒 。
总结,在大型的数据处理中,应该尽量避免在遍历中进行数据的转换,避免用一些原理复杂的函数。如strtotime
我在使用PHP strtotime函数是,遇到了+9时区的问题。 得到的结果是 2024-01-01 09:00:00 请问到底该怎么解决才能使 strtotime 直接得到0点的时间戳? 我检查了系统的时区,timedatectl 返回的结果 PHP date_default_timezone_get() 返回的是PRC。 即便使用 DateTime 得到的依然是2024-01-01 09:00
本文向大家介绍PHP strtotime函数用法、实现原理和源码分析,包括了PHP strtotime函数用法、实现原理和源码分析的使用技巧和注意事项,需要的朋友参考一下 源码位置:\ext\date\php_date.c strtotime函数在使用strtotime(“-1 month”)求上一个月的今天时会出一些状况, 因此也引出写这篇文章,本文包括如下内容: 1).strtotime函数的
本文向大家介绍php中随机函数mt_rand()与rand()性能对比分析,包括了php中随机函数mt_rand()与rand()性能对比分析的使用技巧和注意事项,需要的朋友参考一下 本文实例对比分析了php中随机函数mt_rand()与rand()性能问题。分享给大家供大家参考。具体分析如下: 在php中mt_rand()和rand()函数都是可以随机生成一个纯数字的,他们都是需要我们设置好种子
问题内容: PHP中的strtotime()可以进行以下转换: 输入: 输出: 有没有一种简单的方法可以在Java中做到这一点? 是的,这是重复项。但是,原始问题没有得到回答。我通常需要能够查询过去的日期。我想让用户说“我希望从“ -1周”到“现在”的所有事件”。这将使编写这些类型的请求的脚本变得更加容易。 问题答案: 我试图实现一个简单的(静态)类,该类模仿PHP的某些模式。此类旨在 开放用于修
我真的很喜欢PHP函数strtotime(),但是用户手册没有给出支持的日期格式的完整描述。它只举了几个例子,例如“2000年9月10日”、“+1周2天4小时2秒”和“下星期四”。 我在哪里可以找到完整的描述?
本文向大家介绍php中in_array函数用法分析,包括了php中in_array函数用法分析的使用技巧和注意事项,需要的朋友参考一下 本文实例分析了php中in_array函数用法。分享给大家供大家参考。具体如下: PHP是弱类型语言 在使用IN_ARRAY函数时尽量带上第三个参数,代码如下: 从上面的三个函数可以看出来当,第一个:in_array(0,array('s','sss'),true
本文向大家介绍PHP函数import_request_variables()用法分析,包括了PHP函数import_request_variables()用法分析的使用技巧和注意事项,需要的朋友参考一下 本文实例分析了PHP函数import_request_variables()用法。分享给大家供大家参考,具体如下: import_request_variables 函数可以在 register_
问题内容: 我知道PHP函数的有害行为 时间 例如,向以下日期添加一个月(+1个月):31.01.2011-> 03.03.2011 我 知道这不是正式的 PHP错误 ,并且该解决方案背后有一些争论,但至少对我而言,这种行为造成了很多时间的浪费(过去和现在),我个人讨厌它。 我发现甚至更陌生的是,例如: MySQL: DATE_ADD(‘2011-01-31’,INTERVAL 1 MONTH)返