天涯论坛

 找回密码
 立即注册
搜索
查看: 19|回复: 2

怎么样在不引起服务器宕机的状况下,用 PHP 读取大文件

[复制链接]

2994

主题

220

回帖

9909万

积分

论坛元老

Rank: 8Rank: 8

积分
99099162
发表于 2024-10-4 19:54:02 | 显示全部楼层 |阅读模式

协作翻译

原文:How to Read Big Files with PHP (Without Killing Your Server)

链接:https://www.sitepoint.com/performant-reading-big-files-php/

译者:Tocy, Tony, 南宫冰郁, Tot_ziens

做为PHP研发人员,咱们并不经常需要担心内存管理。PHP 引擎在咱们背面做了很好的清理工作,短期执行上下文的 Web 服务器模型寓意着即使是最潦草的代码不会导致持久的影响。

很少状况咱们可能需要走出这个舒适的地区 ——例如咱们试图在一个大型项目上运行 Composer 来创建我们能够创建的最小的 VPS 时,咱们需要在一个一样小的服务器上读取大文件时。

后面的问题便是咱们将在本教程中深入探讨的。

在 GitHub(https://github.com/sitepoint-editors/sitepoint-performant-reading-of-big-files-in-php) 上能够找到本教程的源码。

衡量成功的标准

保证咱们对代码有改进的独一办法是测试一个欠好状况而后咱们修复之后的测绘与另一个进行比较。换句话说,除非咱们晓得处理方法”对咱们有多大的帮忙倘若有的话),否则咱们晓得是不是真的是一个处理方法

这儿有两个咱们能够关系的衡量标准。首要是CPU运用率。咱们要处理的进程有多快或多慢?第二是内存运用状况。脚本执行时需要多少内存?这两个一般是成反比的 - 这寓意咱们能够以CPU运用率为代价来降低内存运用,反之然。

在一个异步执行模型(如多进程或多线程的PHP应用程序)中,CPU和内存的运用率是很重要的考量原因。在传统的PHP架构中,当任何一个值达到服务器的极限时,这些一般都会作为问题。

测绘PHP内的CPU运用率是不切实质的。倘若这是你要关注的行业,请思虑在Ubuntu或MacOS上运用类似top的工具。针对Windows,请思虑运用Linux子系统,以便在Ubuntu中运用top。

为了本教程的目的,咱们测绘内存运用状况咱们瞧瞧在“传统”的脚本中运用了多少内存。

咱们将执行有些优化策略并对其进行度量。最后,我期盼你能够做出一个有经验的选取

咱们查看内存运用多少的办法是:

咱们将在脚本的最后运用这些函数,以便咱们能够看到哪个脚本一次运用最大的内存。

咱们选取是什么?

这儿非常多办法能够有效地读取文件。然则有两种咱们可能运用它们的情况。咱们想要同期读取和处理所有数据,输出处理过的数据或按照咱们所读取的内容执行其他操作。咱们可能想要转换一个数据流,而不需要真正拜访的数据。

咱们设想一下,针对第1状况咱们期盼读取一个文件,并且每10,000行创建一个独立排队的处理作业。咱们需要在内存中保存最少10000行,并将它们传递给排队的工作管理器(无论采取何种形式)。

针对第二种状况咱们假设咱们想要压缩一个尤其大的API响应的内容。咱们不在乎它的内容是什么,但咱们需要保证它是以压缩形式备份的。

在这两种状况下,倘若咱们需要读取大文件,首要咱们需要晓得数据是什么。第二,咱们并不在乎数据是什么。让咱们来探索这些选取吧...

逐行读取文件

有许多操作文件的函数,咱们把部分结合到一个简单的文件阅读器中(封装为一个办法):

咱们读取一个文本文件为莎士比亚全集。文件体积为5.5MB,内存占用峰值为12.8MB。此刻咱们用一个生成器来读取每一行:

文本文件体积不变,但内存运用峰值只是393KB。即使咱们能把读取到的数据做有些事情并不寓意着什么。咱们能够在看到两条空白时把文档分割成块,像这般

猜到咱们运用了多少内存吗?咱们把文档分割为1216块,仍然只运用了459KB的内存,这是不是让你惊讶?思虑到生成器的性质,咱们运用的最多内存是运用在迭代中咱们需要存储的最大文本块。在本例中,最大的块为101985字符。

已然撰写了运用生成器提示性能Nikita Popov的迭代器库倘若你感兴趣就去瞧瞧吧!

生成器还有其它用途,然则显著的好处便是高性能读取大文件。倘若咱们需要处理这些数据,生成器可能是最好的办法

管道间的文件

咱们不需要处理数据的状况下,咱们能够把文件数据传递到另一个文件。一般叫作为管道(大概是由于咱们看不到除了两端的管子里面,当然,它是不透明的),咱们能够经过运用办法实现。让咱们先写一个脚本从一个文件传到另一个文件。这般咱们能够测绘内存的占用状况

不出所料,这个脚本运用更加多的内存来进行文本文件复制。这是由于它读取(和保存)文件内容在内存中,直到它被写到新文件中。针对小文件这种办法许没问题。当为更大的文件时,就捉襟见肘了…

咱们尝试用流(管道)来传送一个文件到另一个:

这段代码稍微有点陌生。咱们打开了两文件的句柄,第1个是只读模式,第二个是只写模式,而后咱们第1个复制到第二个中。最后咱们关闭了它,许使你惊讶,内存只占用了393KB。

这似乎很熟练。像代码生成器在存储它读到的每一行代码?那是由于第二个参数fgets规定了每行读多少个字节(默认值是-1直到下一行径止)。

第三个参数stream_copy_to_stream和第二个参数是同一类参数(默认值相同),stream_copy_to_stream一次从一个数据流里读一行,同期写到另一个数据流里。它跳过生成器仅有一个值的部分(由于咱们不需要这个值)。

这篇文案针对咱们来讲可能是没用的,因此咱们有些咱们可能会用到的例子。假设咱们想从咱们的CDN中输出一张照片做为一种重定向的路由应用程序。咱们能够参照下边的代码来实现它:

设想一下,一个路由应用程序让咱们看到这段代码。然则咱们想从CDN获取一个文件,而不是从本地的文件系统获取。咱们能够有些其他的东西来更好的替换file_get_contents(就像Guzzle),即使在引擎内部它们几乎是同样的。

照片的内存大概有581K。此刻,让咱们来试试这个

内存运用显著变少(大概400K),然则结果是同样的。倘若咱们不关注内存信息,咱们依旧能够用标准模式输出。实质上,PHP供给了一个简单的方式来完成:

其它流

还有其它有些流,咱们能够经过管道来写入和读取(或只读取/只写入):

php://stdin (只读)

php://stderr (只写, 如php://stdout)

php://input (只读) 这使咱们能够拜访原始请求体

php://output (只写) 让咱们写入输出缓冲区

php://memory 和 php://temp (读-写) 是咱们能够临时存储数据的地区区别之处在于一旦它变得足够大 php://temp 会将数据存储在文件系统中,而 php://memory 将始终持存储在内存中直到资源耗尽。

过滤器

还有一个咱们能够在stream上运用的技巧,叫作为过滤器。它们是一种中间的过程供给对stream数据的有些掌控,但不把她们暴露给咱们。想象一下,咱们运用Zip扩展名来压缩咱们的shakespeare.txt文件。

这是一小段整洁的代码,但它测绘内存占用在10.75MB上下运用过滤器的话,咱们能够减少内存:

此处,咱们能够看到名为php://filter/zlib.deflate的过滤器,它读取并压缩资源的内容。咱们能够在之后将压缩数据导出到另一个文件中。这仅运用了896KB.

晓得这是不同样的格式,制作zip存档是有好处的。你不得不可疑倘若能够选取区别的格式并节省约12倍的内存,为何不选呢?

认识压此数据,咱们能够经过执行另一个zlib filter将压缩后的数据还原:

Streams have been extensively covered in Stream在“理解PHP中的流”和“U有效运用PHP中的流

”中已然被全面介绍了。倘若爱好一个完全区别的视角,能够阅读一下。

定制流

fopen和file_get_contents有它们自己的一套默认选项,然则这些都是完全可定制的。为了定义它们,咱们需要创建一个新的流上下文:

在这个例子中,咱们正在尝试向API发出POST请求。 API终端是安全的,但咱们仍然需要运用http上下文属性(用于http和https)。咱们设置有些信息头参数,并打开一个文件句柄到API。因为上下文处理写操作,咱们能够将句柄打开为只读。

查看文档认识更加多https://php.net/function.stream-context-create

制定自定义协议和过滤器

咱们结束之前,让咱们谈谈制定自定义协议。 倘若你查看文档,你能够找到一个示例类来实现:

咱们不打算实现其中的一个,由于我认为它应该有自己的教程。这儿有很多工作需要完成。然则一旦这个工作完成,咱们能够很容易地注册咱们的流包装:

一样能够创建自定义流过滤器。该文档有一个示例过滤器类:

能够很容易地注册:

明显表示叫作需要匹配新的筛选器类的filtername属性。能够在php://filter/highligh-names/resource=story.txt字符串中运用自定义过滤器。定义过滤器比定义协议要容易得多。由于协议需要处理目录操作,而过滤器只需处理每一个数据块。

倘若你有这个想法,我剧烈意见你尝试创建自定义协议和过滤器。倘若能够将过滤器应用于stream_copy_to_streamoperations,那样即使在运用大容量文件时,你的应用程序能够内存的状况运用。想象一下,编写一个调节体积的图像过滤器或加密的应用程序过滤器。

总结

虽然这不是咱们经常遇到的问题,但在处理大文件时很容易搞砸。在异步应用程序中,当咱们重视小心运用内存的话,很容易引起全部服务器宕机。

本教程期盼向你介绍有些新的想法(让你重新认识她们),以便你可以更加多思虑怎样有效地读取和写入大型文件。当咱们起始熟练流程和生成器,并停止运用像file_get_contents这般的函数时,咱们的应用程序中就会减少错误的类别,这看起来是很好。

举荐阅读

2018 最具就业前景的 7 大编程语言,前三无悬念?

要火!Python 纳入高考科目;PHP、JS 等主流编程语言爆安全漏洞

怎样愉快的运用 MQ - 详述各样功能场景

Java 10 新特性解密,引入类型推断机制

必须 SQL 查找优化技巧,提高网站拜访速度

点击“阅读原文”查看更加多精彩内容





上一篇:2019PHP面试题大全【PHP基本部分】
下一篇:dwg格式文件用什么软件打开?2024帮忙你快速转换文件格式的软件合集
回复

使用道具 举报

3060

主题

2万

回帖

9913万

积分

论坛元老

Rank: 8Rank: 8

积分
99139056
发表于 2024-10-12 08:13:31 | 显示全部楼层
系统提示我验证码错误1500次 \~゛,
回复

使用道具 举报

3060

主题

2万

回帖

9913万

积分

论坛元老

Rank: 8Rank: 8

积分
99139056
发表于 2024-10-13 03:02:51 | 显示全部楼层
你的话语如春风拂面,让我心生暖意。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

站点统计|Archiver|手机版|小黑屋|天涯论坛 ( 非经营性网站 )|网站地图

GMT+8, 2024-11-22 20:24 , Processed in 0.137273 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2023, Tencent Cloud.