PHP的性能大坑--strtotime函数

最近在做一个游戏数据统计后台,最基础的功能是通过分析注册登录日志来展示用户数据。在公司内部测试,用户量很少,所以就没有发现什么性能问题。但是这两天一起放到真实的测试环境,用户量噌噌地就涌进来了,从下午开始,在线人数的统计开始卡,几秒钟才返回数据;注册人数的查询速度还行。到了晚上,在线人数的统计基本上就加载超时打不开了。虽然不知他们游戏端那边什么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;

去掉strtotime的比较

遍历再优化

大家可能发现一个问题,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

时间: 2024-08-05 19:34:41

PHP的性能大坑--strtotime函数的相关文章

js实现类似php中strtotime函数和timetostr的日期转换/互换功能

<script type="text/javascript">   //日期(格式:yyyy-mm-dd H:i:s) ---转换为以秒为单位的unix时间轴(格式:xxxxxx) 方法一:   //摘取天上星:http://blog.csdn.net/zqtsx   function strtotime1(datetime){        var tmp_datetime = datetime.replace(/:/g,'-');        tmp_datetime

PHP strtotime函数详解

先看手册介绍: strtotime — 将任何英文文本的日期时间描述解析为 Unix 时间戳 格式:int strtotime ( string $time [, int $now ] ) 本函数预期接受一个包含美国英语日期格式的字符串并尝试将其解析为 Unix 时间戳(自 January 1 1970 00:00:00 GMT 起的秒数),其值相对于 now 参数给出的时间,如果没有提供此参数则用系统当前时间. 本函数将使用 TZ 环境变量(如果有的话)来计算时间戳.自 PHP 5.1.0 起

mysql常用内置函数-查询语句中不能使用strtotime()函数!

来自:http://yushine.iteye.com/blog/775407 FROM_UNIXTIME把 unix时间戳转换为标准时间 unix_timestamp把标准时间转换为 unix时间戳//查询语句中不能使用strtotime()函数!但是可以使用unix_timestamp DATE_FORMAT('1997-10-04 22:23:00','%Y-%m-%d') 格式化时间 如:select FROM_UNIXTIME(pubdate) from article where p

C#时间转整型(时间戳),模仿php strtotime函数的部分功能

今天需要将一个基于MS SQL数据库的新闻系统数据导入phpcms v9,源系统新闻日期格式为"2014-01-15 10:45:49",而phpcms中使用的是整型时间戳,在php中很简单,用strtotime()即可:在C#中,需要自己写函数,步骤如下: 步骤1.先计算phpcms中时间戳所用基准时间: 1 TimeSpan ts = new TimeSpan(0,0,0,1389753949); 2 DateTime now = Convert.ToDateTime("

谨慎使用php的strtotime()函数

我们在日常业务中,针对业务量,经常会采用对数据库按时间做横向分表,分表后的查询往往会涉及到时间问题.例如,我们想查询某个用户距离当前时间1个月的订单情况,在这个时候,我们有些会用到strtotime()函数去处理. 但是使用strtotime(),需要非常谨慎.我们先看一段代码,代码目的是想拿到几个月以前的年份月份,例如今天是2014年8月1号,我想拿到2个月前的年份月份是 array("0"=>"201406", "1"=>&quo

PHP用strtotime()函数比较两个时间的大小实例详解

在PHP开发中,我们经常会对两个时间的大小进行判断,但是,在PHP中,两个时间是不可以直接进行比较,因为时间是由年.月.日.时.分.秒组成的,所以,如果需要将两个时间进行比较的话,我们首先要做的就是将时间解析为时间戳的格式,这就要用到我们前面学习的利用strtotime()函数将日期和时间解析为UNIX时间戳的知识了,只有将时间转化为时间戳的格式,才能够进行比较.本章就给大家讲解一下,在PHP中,怎么比较两个时间的大小. 假如现在有两个时间: 2017-4-15 2018-4-15 我们首先就要

js前端性能优化之函数节流和函数防抖

前言:针对一些会频繁触发的事件如scroll.resize,如果正常绑定事件处理函数的话,有可能在很短的时间内多次连续触发事件,十分影响性能 节流: 节流:使得一定时间内只触发一次函数. 它和防抖动最大的区别就是,节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而防抖动只是在最后一次事件后才触发一次函数. 原理是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器 主要有两种实现方法: 时间戳 定时器 时间戳实现:

PHP利用strtotime函数打印指定日期

打印某一日期的前一天 echo date("Y-m-d",(strtotime("2009-01-01") - 3600*24)); (1)打印明天此时的时间戳strtotime(”+1 day”) 当前时间:echo date(”Y-m-d H:i:s”,time()) //结果:2009-01-22 09:40:25 指定时间:echo date(”Y-m-d H:i:s”,strtotime(”+1 day”)) //结果:2009-01-23 09:40:2

性能大坑

在实现某个功能的过程中经常造成了一些crash的情况,解决了crash之后再跑benchmark,发现性能慢了很多.对比代码历史没有任何性能相关的改动.在调试器里面看到FTH Heap Shim... 信息,于是网上搜索,发现对于经常crash的进程,Windows会对其进行监控.尝试在命令行运行以下命令充值FTH进程列表: Rundll32.exe fthsvc.dll,FthSysprepSpecialize 之后性能问题解决. 具体参考: https://msdn.microsoft.co