BufferedReader 如何从 S3 读取文件?
Posted
技术标签:
【中文标题】BufferedReader 如何从 S3 读取文件?【英文标题】:How does BufferedReader read files from S3? 【发布时间】:2019-01-01 12:04:39 【问题描述】:我在 AWS S3 中有一个非常大的文件(几 GB),我只需要文件中满足特定条件的少量行。我不想将整个文件加载到内存中,然后搜索并打印那几行 - 这样做的内存负载太高了。正确的方法是只在内存中加载那些需要的行。
根据 AWS 文档to read from file:
fullObject = s3Client.getObject(new GetObjectRequest(bucketName, key));
displayTextInputStream(fullObject.getObjectContent());
private static void displayTextInputStream(InputStream input) throws IOException
// Read the text input stream one line at a time and display each line.
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String line = null;
while ((line = reader.readLine()) != null)
System.out.println(line);
System.out.println();
这里我们使用BufferedReader。我不清楚下面发生了什么。
我们是否在每次读取新行时都对 S3 进行网络调用,并且只将当前行保留在缓冲区中?还是将整个文件加载到内存中,然后由 BufferedReader 逐行读取?还是介于两者之间?
【问题讨论】:
来自link you posted:注意您的网络连接保持打开状态,直到您读取所有数据或关闭输入流。我们建议您尽快阅读直播内容。 我的问题更多的是——将整个文件加载到内存中,还是只加载我正在阅读的行,或者介于两者之间的缓冲区? 只需编写一个小示例应用程序并尝试使用上述代码从S3读取文件。如果它会立即将孔文件读入内存,您肯定会遇到OOM。 【参考方案1】:仅对 AWS 基础设施进行一次 HTTP 调用,数据以小块的形式读入内存,其大小可能会有所不同,并且不受您的直接控制。
假设文件中的每一行都是相当小的大小,这已经非常节省内存。
假设您的“某些条件”是简单的字符串匹配,进一步优化(针对网络和计算资源)的一种方法是使用 S3 Select:https://aws.amazon.com/s3/features/#s3-select
【讨论】:
【参考方案2】:您链接的文档中已经给出了您问题的答案之一:
在您读取所有数据或关闭输入流之前,您的网络连接保持打开状态。
BufferedReader
不知道它读取的数据来自哪里,因为您将另一个 Reader
传递给它。 BufferedReader
创建一个特定大小(例如 4096 个字符)的缓冲区,并在开始分发 read()
或 read(char[] buf)
的调用数据之前通过从底层 Reader
读取来填充此缓冲区。
您传递给BufferedReader
的Reader
- 顺便说一下 - 使用另一个缓冲区为自己执行从基于byte
的流到基于char
的读取器的转换。它的工作方式与BufferedReader
相同,因此内部缓冲区通过读取传递的InputStream
来填充,InputStream
是您的 S3 客户端返回的InputStream
。
如果您尝试从流中加载数据,此客户端中究竟会发生什么取决于实现。一种方法是保持打开一个网络连接,您可以根据需要从中读取,或者可以在读取大量数据后关闭网络连接,并在您尝试获取下一个数据时打开一个新连接。
上面引用的文档似乎说我们在这里遇到了前一种情况,所以:不,readLine
的调用不会导致单个网络调用。
并回答您的另一个问题:不,BufferedReader
、InputStreamReader
和很可能由 S3 客户端返回的 InputStream
没有将整个文档加载到内存中。这将与首先使用流的整个目的相矛盾,并且 S3 客户端可以简单地返回 byte[][]
(以达到每个 byte
-array 的 2^32 字节的限制)
编辑:最后一段有一个例外。如果整个千兆字节的大文档没有换行符,调用readLine
实际上会导致将整个数据读入内存(并且很可能会导致 OutOfMemoryError)。在回答您的问题时,我假设了一个“常规”文本文档。
【讨论】:
这句话对我来说似乎不清楚 - “不,readLine 的调用不会导致单个网络调用。”。您是否建议每次调用 readLine api 进行网络调用? @ArshanQureshi 该特定声明是上述内容的摘要。readLine
的读取不会导致网络调用,因为在您和 S3 存储桶之间存在基于字节的缓冲区(至少根据文档看起来是这样),该缓冲区独立于实际数据填充,您只需要调用 readLine
时该缓冲区的一部分(直到出现换行符)。【参考方案3】:
如果您基本上不是在搜索特定的单词/单词,并且您知道字节范围,您还可以在 S3 中使用 Range 标头。当您处理几个 GB 大小的单个文件时,这应该特别有用。指定范围不仅有助于减少内存,而且速度更快,因为只读取文件的指定部分。
见Is there "S3 range read function" that allows to read assigned byte range from AWS-S3 file?
希望这会有所帮助。
Sreram
【讨论】:
【参考方案4】:取决于文件中行的大小。 readLine() 将继续构建从流中获取数据的字符串,块大小为缓冲区大小,直到您遇到行终止字符。所以使用的内存将是你的行长度+缓冲区长度的顺序。
【讨论】:
以上是关于BufferedReader 如何从 S3 读取文件?的主要内容,如果未能解决你的问题,请参考以下文章
Java,为啥从 MappedByteBuffer 读取比从 BufferedReader 读取慢
如何将 s3 数据从一个 EMR 集群读取到另一个 EMR 集群?