摘要:開(kāi)發(fā)中經(jīng)常遇到這樣的場(chǎng)景產(chǎn)品汪我要在后臺(tái)做一個(gè)功能,可以導(dǎo)出自定義時(shí)間范圍的訂單信息。結(jié)果,第二天一上班產(chǎn)品汪過(guò)來(lái)就是拍桌子,我想把去年一整年的訂單都導(dǎo)出來(lái),結(jié)果后臺(tái)直接就掛了開(kāi)發(fā)小哥一查,原來(lái)是內(nèi)存溢出了,一年下來(lái)的的訂單量足足有條。
開(kāi)發(fā)中經(jīng)常遇到這樣的場(chǎng)景
產(chǎn)品汪:我要在后臺(tái)做一個(gè)功能,可以導(dǎo)出自定義時(shí)間范圍的訂單信息。開(kāi)發(fā)小哥二話不說(shuō),半天就把功能做完并上線了。結(jié)果,第二天一上班產(chǎn)品汪過(guò)來(lái)就是拍桌子:MD,我想把去年一整年的訂單都導(dǎo)出來(lái),結(jié)果后臺(tái)直接就掛了!
開(kāi)發(fā)小哥一查,原來(lái)是內(nèi)存溢出了,一年下來(lái)的的訂單量足足有1000W條。于是,開(kāi)發(fā)小哥跟產(chǎn)品汪吵了起來(lái):你TM色不色傻,1000W的數(shù)據(jù)你導(dǎo)出來(lái)干diao,你是不是想把服務(wù)器給搞掛掉?
于是,產(chǎn)品汪與程序狗就這么結(jié)下梁子了。
但是。產(chǎn)品的需求你敢這樣懟回去,那如果是老板提的這個(gè)需求呢,就是硬要把1000W條記錄導(dǎo)出來(lái),你還不得乖乖回去碼代碼?
那么,這個(gè)如果真要導(dǎo)出這大量數(shù)據(jù),該怎么做呢?開(kāi)發(fā)中我們經(jīng)常會(huì)使用框架來(lái)提高我們的開(kāi)發(fā)效率,但也意味著框架會(huì)對(duì)一些數(shù)據(jù)進(jìn)行封裝。
比如Yii2,當(dāng)我們想獲取去年一年的訂單時(shí),我們的代碼會(huì)這樣寫:
Order::find()->where("create_time between "2016-01-01" AND "2016-12-31"")->all();
當(dāng)我們將上面代碼得到的結(jié)果集再拿去遍歷時(shí),數(shù)據(jù)量一大,就會(huì)內(nèi)存溢出。
原因是:Yii2中,對(duì)all方法進(jìn)行了封將,將大量的數(shù)據(jù)存入了數(shù)組中,而遍歷大數(shù)據(jù),必然會(huì)導(dǎo)致內(nèi)存迅速上升。
那如何取出大量數(shù)據(jù),而又不存到數(shù)組中呢?這就要用到了PHP中的迭代器:Iterator。如果有看過(guò)PDO::query的返回值類型的話,我們會(huì)發(fā)現(xiàn),這個(gè)方法返回的PDOStatement,正是對(duì)Iterator的實(shí)現(xiàn)。關(guān)于Iterator,請(qǐng)自行腦補(bǔ)。
即然框架幫我們做了多余的封裝,那么我們就改用原生API來(lái)實(shí)現(xiàn)。以下是完整代碼
$sql = "select * from user"; $pdo = newPDO( "mysql:host=127.0.0.1;dbname=test", "root", "root" ); $pdo->setAttribute( PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false); $rows = $pdo->query($sql); $filename = date("Ymd").".csv";//設(shè)置文件名 header("Content-Type:text/csv"); header("Content-Disposition:attachment;filename={$filename}"); $out = fopen("php://output","w"); fputcsv( $out, [ "id", "username", "password", "create_time" ] ); foreach( $rows as $row ){ $line=[ $row["id"], $row["username"], $row["password"], $row["create_time"] ]; fputcsv($out,$line); } fclose($out); $memory=round((memory_get_usage()-$startMemory)/1024/1024,3)."M".PHP_EOL; file_put_contents("/tmp/test.txt",$memory,FILE_APPEND);
在表中生成7位數(shù)量級(jí)的記錄,執(zhí)行上面的代碼,通過(guò)查看內(nèi)存使用,發(fā)現(xiàn)整個(gè)過(guò)程只占用了0.XM的內(nèi)存,完全沒(méi)有任何內(nèi)存溢出的現(xiàn)象。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/31623.html
摘要:環(huán)境適用場(chǎng)景需要處理一定數(shù)據(jù)集業(yè)務(wù)從讀取一定數(shù)據(jù)的業(yè)務(wù)導(dǎo)出一次需要處理一定的業(yè)務(wù)操作更新刪除等更多需要處理一定數(shù)據(jù)集的操作關(guān)鍵設(shè)置關(guān)鍵設(shè)置,如果不設(shè)置,依舊會(huì)從一次取出數(shù)據(jù)到里的游標(biāo)屬性不是必須的生成器生成器,迭代數(shù)據(jù)操作本生成器可 環(huán)境 mysql: 5.6.34php: 5.6nginx: php-fpm 適用場(chǎng)景 需要處理一定數(shù)據(jù)集業(yè)務(wù) 從mysql讀取一定數(shù)據(jù)的業(yè)務(wù)導(dǎo)出 一次...
摘要:它最簡(jiǎn)單的調(diào)用形式看起來(lái)像一個(gè)申明,不同之處在于普通會(huì)返回值并終止函數(shù)的執(zhí)行,而會(huì)返回一個(gè)值給循環(huán)調(diào)用此生成器的代碼并且只是暫停執(zhí)行生成器函數(shù)。 0x01 寫在前面 本文主要介紹: Generator的簡(jiǎn)單用法。 Generator的底層實(shí)現(xiàn)。 本文比較長(zhǎng),可能會(huì)耗費(fèi)你比較多的時(shí)間。如果你比較了解Generator的用法,僅想了解底層實(shí)現(xiàn),可以直接跳到底層實(shí)現(xiàn)部分。 本文分析的PH...
閱讀 1352·2019-08-30 15:44
閱讀 1414·2019-08-29 18:42
閱讀 464·2019-08-29 13:59
閱讀 803·2019-08-28 17:58
閱讀 2848·2019-08-26 12:02
閱讀 2448·2019-08-23 18:40
閱讀 2438·2019-08-23 18:13
閱讀 3138·2019-08-23 16:27