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地点:北京国际饭店会议中心
随机推荐
- Linux 错误集锦
1. CentOS 7 运行yum时出现/var/run/yum.pid已被锁定,PID为xxxx的另一个程序正在运行的问题解决 解决办法: rm -f /var/run/yum.pid,删除文件后再 ...
- [poj] Catch That Cow--bfs
Description Farmer John has been informed of the location of a fugitive cow and wants to catch her i ...
- Solr 6.7学习笔记(02)-- 配置文件 managed-schema (schema.xml) - Analyzer, tokenizer(4)
有些时候,我们需要自定义 fieldType.下面的例子就是自定义的 fieldType,<analyzer type="index"> 表示索引时怎么处理,<a ...
- cogs1612. 大话西游
1612. 大话西游 http://www.cogs.pro/cogs/problem/problem.php?pid=1612 ★★ 输入文件:westward.in 输出文件:westwa ...
- 洛谷P3645 [APIO2015]雅加达的摩天楼(最短路+分块)
传送门 这最短路的建图怎么和网络流一样玄学…… 一个最朴素的想法是从每一个点向它能到达的所有点连边,边权为跳的次数,然后跑最短路(然而边数是$O(n^2)$除非自创复杂度比spfa和dijkstra还 ...
- How to install your SSL Certificate to your Windows Server
Installation: Open the ZIP file containing your certificate. Save the file named your_domain_name.ce ...
- 【实验吧】该题不简单——writeup
题目地址:http://ctf5.shiyanbar.com/crack/3/ 一定要注意读题: 要求找出用户名为hello的注册码,这八成就是 要写注册机啊! ——————————————————— ...
- String,StringBuffer和StringBuilder
String,StringBuffer和StringBuilder分别应该在什么情况下使用? String 是Java的字符串类,其实质上也是用Char类型存储的,但是除了hash属性,其他的属性都声 ...
- 1081 Rational Sum(20 分)
Given N rational numbers in the form numerator/denominator, you are supposed to calculate their sum. ...
- 057 Insert Interval 插入区间
给出一个无重叠的按照区间起始端点排序的区间列表.在列表中插入一个新的区间,你要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间).示例 1:给定区间 [1,3],[6,9],插入并合并 ...