我在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逐行读取?还是介于两者之间?
答案 0 :(得分:7)
您链接的文档中已经给出了您问题的答案之一:
您的网络连接保持打开状态,直到您读取所有数据或关闭输入流为止。
BufferedReader
不知道读取的数据来自何处,因为您要向其中传递另一个Reader
。 BufferedReader
创建一个特定大小的缓冲区(例如4096个字符),并在开始分发Reader
或{{1 }}。
您传递给read()
的{{1}}是-本身使用另一个缓冲区来完成从基于read(char[] buf)
的流到Reader
的转换。的读者。它的工作方式与BufferedReader
相同,因此通过读取传递的byte
(即S3客户端返回的char
)来填充内部缓冲区。
如果您尝试从流中加载数据,则在此客户端中发生的实际情况取决于实现。一种方法是保持打开一个网络连接,然后您可以根据需要从中读取数据,或者在读取大量数据并尝试获取下一个数据时打开一个新的网络连接,然后关闭该网络连接。 / p>
上面引用的文档似乎表明我们遇到了前一种情况,因此:不,BufferedReader
的调用不会导致单个网络调用。
并回答您的另一个问题:不,由S3客户端返回的InputStream
,InputStream
和最有可能的readLine
不会将整个文档加载到内存中。首先,这将与使用流的整个目的相矛盾,并且S3客户端可以简单地返回一个BufferedReader
(来限制每个InputStreamReader
数组2 ^ 32字节的限制)
编辑:最后一段是一个例外。如果整个千兆字节的大文档没有换行符,则调用InputStream
实际上会导致将整个数据读入内存(并且很可能会导致OutOfMemoryError)。在回答您的问题时,我假设使用了“常规”文本文档。
答案 1 :(得分:3)
如果您基本上不是在搜索特定词,并且知道字节范围,则也可以在S3中使用Range标头。当您使用几个GB大小的单个文件时,这应该特别有用。指定范围不仅有助于减少内存,而且速度更快,因为仅读取文件的指定部分。
请参见Is there "S3 range read function" that allows to read assigned byte range from AWS-S3 file?
希望这会有所帮助。
Sreram
答案 2 :(得分:0)
取决于文件中各行的大小。 readLine()将继续构建从缓冲区中以缓冲区大小为大小的块中获取数据的字符串,直到遇到行终止符为止。因此,使用的内存将取决于您的行长+缓冲区长度。
答案 3 :(得分:0)
仅对AWS基础设施进行一次HTTP调用,并且将数据以小块的形式读入内存,其大小可能有所不同,并且不受您的直接控制。
假设文件中的每一行都是相当小的大小,这已经非常节省了内存。
一种进一步优化(针对网络和计算资源)的方法是,假设您的“确定条件”是简单的字符串匹配,则使用S3选择:https://aws.amazon.com/s3/features/#s3-select