php中strtotime函數(shù)性能分析
最近在做一個(gè)游戲數(shù)據(jù)統(tǒng)計(jì)后臺(tái),最基礎(chǔ)的功能是通過(guò)分析注冊(cè)登錄日志來(lái)展示用戶數(shù)據(jù)。在公司內(nèi)部測(cè)試,用戶量很少,所以就沒(méi)有發(fā)現(xiàn)什么性能問(wèn)題。但是這兩天一起放到真實(shí)的測(cè)試環(huán)境,用戶量噌噌地就涌進(jìn)來(lái)了,從下午開(kāi)始,在線人數(shù)的統(tǒng)計(jì)開(kāi)始卡,幾秒鐘才返回?cái)?shù)據(jù);注冊(cè)人數(shù)的查詢速度還行。到了晚上,在線人數(shù)的統(tǒng)計(jì)基本上就加載超時(shí)打不開(kāi)了。雖然不知他們游戲端那邊什么BUG,玩家那邊登錄經(jīng)常出問(wèn)題,導(dǎo)致在線人數(shù)和注冊(cè)人數(shù)并不是很多。但是就這一點(diǎn)數(shù)據(jù)量我這邊查詢的速度也不行,這就很尷尬了。
現(xiàn)在他們那邊在查游戲的BUG,我這邊也在看統(tǒng)計(jì)后臺(tái)的代碼到底性能出在哪里。首先說(shuō)明一下,我統(tǒng)計(jì)用的數(shù)據(jù)是從庫(kù),他們游戲用的是主庫(kù),再說(shuō)我這邊管理員人數(shù)就幾個(gè),不可能會(huì)影響到游戲服的性能問(wèn)題。
今天項(xiàng)目組長(zhǎng)把數(shù)據(jù)庫(kù)都導(dǎo)過(guò)來(lái)到公司內(nèi)的服務(wù)器。我拷了一份到本機(jī),看看統(tǒng)計(jì)平臺(tái)的性能問(wèn)題出在哪里。然后卻發(fā)現(xiàn),居然連注冊(cè)統(tǒng)計(jì)都非??ǎ?wù)器上是兩秒左右返回,本機(jī)要二十幾秒,還經(jīng)常超時(shí)(PHP的默認(rèn)配置是30秒超時(shí));在線統(tǒng)計(jì)的就不用說(shuō)了肯定打不開(kāi)??戳艘幌聰?shù)據(jù)庫(kù),當(dāng)天的注冊(cè)記錄也就 3500 條左右(有假數(shù)據(jù)),每五分鐘統(tǒng)計(jì)一次,一天就是統(tǒng)計(jì) 288 次。當(dāng)然這里肯定不是循環(huán)查詢數(shù)據(jù)庫(kù)288次,那樣會(huì)被罵死的吧。
統(tǒng)計(jì)時(shí)間段內(nèi)的注冊(cè)數(shù),邏輯也非常簡(jiǎn)單,就是每個(gè)時(shí)間段遍歷一次數(shù)據(jù),比較時(shí)間大小,符合就+1。但是為什么這么簡(jiǎn)單的邏輯,也就一百萬(wàn)次循環(huán),怎么會(huì)跑出了足足半分鐘的時(shí)間那么久呢?
關(guān)鍵問(wèn)題就出在于 時(shí)間比較這里了,我們都知道,時(shí)間戳是比較時(shí)間大小的一個(gè)比較科學(xué)的方法,而數(shù)據(jù)庫(kù)里記錄的時(shí)間一般都是以 YYYY-mm-dd HH:ii:ss 的形式,PHP里有strtotime的函數(shù)轉(zhuǎn)換成時(shí)間戳。然而在288個(gè)for * 3500個(gè)foreach 的加持之后,這里的執(zhí)行時(shí)間長(zhǎng)達(dá)半分鐘。
$nowDayDT = strtotime( date('Y-m-d') ); $__startT = microtime(TRUE); for($i=0; $i<$allTime; $i += $gapTime){ $count = 0; //用于數(shù)據(jù)比較的 $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;
那這樣的話,基本上是沒(méi)辦法再用這個(gè)strtotime的函數(shù)的了,那還有什么辦法比較時(shí)間大小呢?答案很簡(jiǎn)單粗暴,PHP里面可以直接比較兩個(gè)日期時(shí)間字符串!所以改過(guò)后的代碼如下。然后現(xiàn)在的運(yùn)行時(shí)間大概是 0.3秒
$__startT = microtime(TRUE); for($i=0; $i<$allTime; $i += $gapTime){ $count = 0; //用于數(shù)據(jù)比較的 $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;
遍歷再優(yōu)化
大家可能發(fā)現(xiàn)一個(gè)問(wèn)題,for 里面嵌套一個(gè) foreach,這性能有點(diǎn)擔(dān)憂,其中里面的 foreach 有必要完全遍歷嗎?其實(shí)是不必的。只要查SQL數(shù)據(jù)的時(shí)候,按時(shí)間排序排出來(lái)。優(yōu)化后的時(shí)間比較算法如下:
for{ ... foreach($rawData as $line){ $time = $line['log_dt'];//strtotime($line['log_dt']); //優(yōu)化算法計(jì)算 if($time<$startDT) continue; //小于開(kāi)始時(shí)間則跳過(guò) if($time>=$endDT) break; //大于結(jié)束時(shí)間則結(jié)束 $count ++; //否則為符合條件 //原始的算法 // if( $startDT<=$time && $time<$endDT ){ // $count ++; // } } ...}
這里巧用了 continue 和 break 關(guān)鍵字,用于跳過(guò)一次循環(huán)和結(jié)束整個(gè)循環(huán)。這次的話,一天中剛開(kāi)始的時(shí)間統(tǒng)計(jì)中,后面很大一部分?jǐn)?shù)據(jù)的都可以直接跳過(guò)。最后總遍歷時(shí)間縮短為約0.12秒 。
總結(jié),在大型的數(shù)據(jù)處理中,應(yīng)該盡量避免在遍歷中進(jìn)行數(shù)據(jù)的轉(zhuǎn)換,避免用一些原理復(fù)雜的函數(shù)。如strtotime
相關(guān)文章
PHP+JS實(shí)現(xiàn)大文件切片上傳功能實(shí)現(xiàn)實(shí)例源碼
這篇文章主要介紹了PHP+JS實(shí)現(xiàn)大文件切片上傳功能實(shí)現(xiàn)實(shí)例源碼,需要的朋友可以參考下2023-05-05php將一維數(shù)組轉(zhuǎn)換為每3個(gè)連續(xù)值組成的二維數(shù)組
這篇文章主要介紹了php將一維數(shù)組轉(zhuǎn)換為每3個(gè)連續(xù)值組成的二維數(shù)組的方法,涉及array_slice函數(shù)的使用技巧,需要的朋友可以參考下2016-05-05取得單條網(wǎng)站評(píng)論以數(shù)組形式進(jìn)行輸出
這篇文章主要介紹了取得單條網(wǎng)站評(píng)論方法并以數(shù)組形式進(jìn)行輸出,需要的朋友可以參考下2014-07-07