2018.2.12 PHP 如何读取一亿行的大文件
PHP 如何读取一亿行的大文件
我们可能在很多场景下需要用 PHP 读取大文件,之后进行处理,如果你没有相关的经验可以看下,希望能给你带来一些启发。
模拟场景
我们有一个 1亿 行,大小大概为 3G 的日志文件,需要分析每一行获取一个 ID,然后拿这些 ID 逐行向数据库发起查询。
先想想 ...
遇到此类的问题稍微有点经验的程序员就需要考虑如下一些问题:
由于 PHP 可以利用的内存有限,即使可以修改我们也不要随便更改这个配置,就用默认的好了,由此可以确定这里肯定不能一次读完,需要考虑逐行分块读取
使用什么方法读比较合理?
思路
• 读写一个文件通常处理的流程是【打开】、【写入|读取】、【关闭】
• 我们知道 PHP 读取文件支持curl、file_get_contents、fopen,前两个对请求远程文件支持的比较好,可以一次性把结果读取到一个字符串里,里面封装了 http 请求以及读写文件完整流程的一些方法,使得我们读取文件非常方便,一个函数搞定,而另外一个fopen就把所有操作权都给你了,你需要怎么读怎么写自己组合
• 拿到 ID 后需要控制对数据库查询的次数,每行分析出 ID 就查数据库也是不合理的,如果考虑把所有 ID 拿到,整体一次请求数据库也是不现实的,PHP 变量可以控制的内存有限,而且数据库也不可能让你一次查询 3G 的 SQL 啊
我们不需要读取每行数据都执行打开、关闭文件,这种开销是巨大的,基于以上考虑,只有 fopen 可选了,fopen 可以分步骤进行操作,对于向数据库查询的操作,我们应该尽量控制单次请求 SQL 的 ID 数量在一个合理的范围,所以脑子里大概有这么个东西
<?php // 1、打开日志文件 // 2、循环读取每一行,保持打开状态 while { // 2.1、获取每一行数据,分析出我们需要的 ID // 2.2、拿着 ID 去数据库进行查询 # 由于是连接到数据库,我们也需要考虑操作数据库的连接、断开等开销成本 # 这里应该实现每查询一批 ID,批次的向数据库进行查询,以确保减少这部分的开销 } // 3、关闭日志文件
开始动手了
模拟机器配置
对于读写来说机器的配置及运行环境很重要,下面介绍下我的试验环境
CPU:4核 Intel(R) Core(TM) i7-4750HQ CPU @ 2.00GHz内存:2G硬盘:PCI-E SSD
PHP INFO
$ php -vPHP 7.1.8 (cli) (built: Aug 4 2017 18:59:36) ( NTS )Copyright (c) 1997-2017 The PHP GroupZend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies$ php -i | grep memory_limitmemory_limit => 128M => 128M
测试写入
我们使用以下脚本 write.php 写入到 test.log 中
<?php$start = date("Y-m-d H:i:s");$line = 0; // 行数$count = 0; // 行计数器$str = ""; // 初始写入的字符串$file = fopen("./test.log", "w");while($line <= 100000000){ ++$line; ++$count; $str .= "The line number is " . $line . "\n"; // 为了减少 fwrite 的开销,我们每累计 200 万行写入一次 // 为啥是 200 万?试了 300 万就超出内存限制了,如果这个可以接受就这个了,不行再调整 if($count == 2000000){ fwrite($file, $str); // 重置行计数、字符串 $str = ""; $count = 0; }}echo "start:" . $start . ",end:" . date("Y-m-d H:i:s") . "\n";
执行,验证是不是写的没问题
// 执行写入脚本$ php write.phpstart:2017-10-30 00:24:40,end:2017-10-30 00:26:05// 查看日志大小,du -sh 是展示一个人类能看得懂的文件大小,灰常有用$ du -sh test.log2.7G test.log// 查看日志多少行$ wc -l test.log100000000 test.log// 查看日志前 5 行$ head -5 test.logThe line number is 1The line number is 2The line number is 3The line number is 4The line number is 5// 查看日志倒数 5 行$ tail -n 5 test.logThe line number is 99999996The line number is 99999997The line number is 99999998The line number is 99999999The line number is 100000000
可以看到写入大概执行了一分半左右,这个速度还可以接受吧,接受不了你自己调整那个 200 万
测试读取
我们的读取脚本 read.php 如下
<?php$start = date('Y-m-d H:i:s');$count = 0; // 计数器,累计到一定值重置$ids = ""; // ID 集合// 打开文件$file = fopen('./test.log', 'r');while(!feof($file)){ // 获取当前指针行的数据,stream_get_line 的作用和 fgets 类似,不过这个可以指定换行符 $current = stream_get_line($file, 1024, "\n"); if($current){ ++$count; // 拼接需要查询的 ID,假设你要获取的是最后一个空格之后的值 $ids .= "," . substr($current, strrpos($current, " ")+1); // 为啥这里是 1000 ?多了不更快么? // 是的,对于向数据库发起查询操作拼接 sql 来说 in 的 id 数量多少也很重要,我这边实验的是 1000 个 id 查询一次,并没有考虑太多 // 如果实际用到这种还需要结合 sql 的 max_allowed_packet 更多的考量这里的值怎么样是比较合理的 if($count == 1000){ $sql = "select id from table where id in(" . ltrim($ids, ",") . ")"; // 查数据库 $count = 0; $ids = ""; } }}// 关闭文件fclose($file);echo 'start:' . $start . ',end:' . date('Y-m-d H:i:s') . "\n";
执行
$ php read.phpstart:2017-10-30 00:30:18,end:2017-10-30 00:30:49
实验了多次基本读取时间维持在半分钟左右,这个速度我觉得还行 ...
总结
以我的配置来说,对于 3G 的文件写入时间大概在一分半钟左右,读取时间在半分钟左右,也就是说类似的需求在如今 SSD 这么便宜的情况下做一些大文件读取的任务,PHP 读取的过程不会受限太多,随着数据量的增大,while 里面的每一个函数、每一个方法的使用都显得尤为重要,举例来说,在做实验的过程中用到了 echo 函数,如果去掉就会减少很多倍的时间。
2018.2.12 PHP 如何读取一亿行的大文件的更多相关文章
- Pandas 读取超过 65536 行的 Excel 文件
Excel 文件的格式曾经发生过一次变化,在 Excel 2007 以前,使用扩展名为 .xls 格式的文件,这种文件格式是一种特定的二进制格式,最多支持 65,536 行,256 列表格.从 Exc ...
- PHP读取大文件的几种方法介绍
读取大文件一直是一个头痛的问题,我们像使用php开发读取小文件可以直接使用各种函数实现,但一到大文章就会发现常用的方法是无法正常使用或时间太长太卡了,下面我们就一起来看看关于php读取大文件问题解决办 ...
- 用Python读取大文件
通常我们在读取文件的时候,会用到read(), readline(), readlines(). 通常可能会有这样的用法: def test1(): with open("/tmp/test ...
- php -- 读取大文件
在PHP中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file.file_get_contents之类的函数,简简单单的几行代码就能 很漂亮的完成我们所需要的功能.但当所操作的文件是一个比较大的 ...
- 【Python】实现对大文件的增量读取
背景 前段时间在做一个算法测试,需要对源于日志的数据进行分析才能获取到结果:日志文件较大,所以想要获取数据的变化曲线,增量读取是最好的方式. 网上有很多人的技术博客都是写的用for循环readline ...
- 新手C#SQLServer在程序里实现语句的学习2018.08.12
从C#中连接到SQL Server数据库,再通过C#编程实现SQL数据库的增删改查. ado.net提供了丰富的数据库操作,这些操作可以分为三个步骤: 第一,使用SqlConnection对象连接数据 ...
- 2018年12月8日广州.NET微软技术俱乐部活动总结
吕毅写了一篇活动总结,写得很好!原文地址是:https://blog.walterlv.com/post/december-event-microsoft-technology-salon.html ...
- 2018.5.12 storm数据源kafka堆积
问题现象: storm代码依赖4个源数据topic,2018.5.12上午8点左右开始收到告警短信,源头的4个topic数据严重堆积. 排查: 1.查看stormUI, storm拓扑结构如下: 看现 ...
- Artificial Intelligence Computing Conference(2018.09.12)
时间:2018.09.12地点:北京国际饭店会议中心
随机推荐
- HDU - 1535 Invitation Cards 前向星SPFA
Invitation Cards In the age of television, not many people attend theater performances. Antique Come ...
- Java中的Synchronized关键字用法
认识synchronized 对于写多线程程序的人来说,经常碰到的就是并发问题,对于容易出现并发问题的地方加上synchronized修饰符基本上就搞定 了,如果说不考虑性能问题的话,这一招绝对能应对 ...
- 缓存测试分享篇:如何利用测试环境进行灰度测试缓存迁移solo
此文已由作者王婷英授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 缓存,看到这两个字,第一反应,最近怎么又要弄缓存的改造啊,这个测试好复杂,一不不留心就踩一个线上bug.实在 ...
- SEO优化之——rel=canonical(整合重复页)
用法一 如图所示 有三个页面,三个页面的访问路径都不一样,但是现实的内容page2和page3显示的内容和page1一样,这时候在三个page中分别都加入了<link res="can ...
- js call apply bind
call.apply.bindcat.call(dog, a, b) == cat.apply(dog, [a, b]) == (cat.bind(dog, a, b))() 1.作用 改变函数内的t ...
- 洛谷P4239 【模板】多项式求逆(加强版)(多项式求逆)
传送门 咱用的是拆系数\(FFT\)因为咱真的不会三模数\(NTT\)-- 简单来说就是把每一次多项式乘法都改成拆系数\(FFT\)就行了 如果您还不会多项式求逆的左转->这里 顺带一提,因为求 ...
- Mac下磁盘无法抹除问题解决
安装CentOS到扩容卡,每次安装都会造成bootcamp分区的windows出问题,遂安装ubantu,结果扩容卡有问题-->无法读取您的磁盘,打开磁盘工具无法抹除,由于无法读取在window ...
- C# ExpandoObject用法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...
- Codeforces Round #565 (Div. 3) C. Lose it!
链接: https://codeforces.com/contest/1176/problem/C 题意: You are given an array a consisting of n integ ...
- CodeForces - 508B-Anton and currency you all know
Berland, 2016. The exchange rate of currency you all know against the burle has increased so much th ...