在对the solution的问题进行downloading a huge dynamic zip with low RAM impact编码时,一个想法开始围困我,并引发了这个问题,要求纯粹的好奇心/对知识的渴望:
如果我不是一次加载InputStream
一个(对数据库进行单独查询),而是将所有InputStream
加载到一个数据库中,我可以遇到什么样的缺点?查询,返回( n ,可能是数千,“已打开”)的列表InputStreams
?
当前(安全)版本: n 查询,一次实例化一个 inputStream
for (long id : ids){
InputStream in = getMyService().loadStreamById(id);
IOUtils.copyStream(in, out);
in.close();
}
假设版本:一个查询, n 实例化inputStreams
List<InputStream> streams = getMyService().loadAllStreams();
for (InputStream in : streams){
IOUtils.copyStream(in, out);
in.close();
in = null;
}
第二种方法的优缺点是什么,不包括用于保持多个java InputStream实例化的(我猜很少)内存量?
是否会导致某种网络冻结或数据库压力(或锁定,或者如果其他人读取/写入Stream指向的相同BLOB字段等问题,等等)多于多个查询?
或者他们是否足够聪明,在被要求提供数据之前几乎不可见,然后1 query + 1000 active stream
可能比1000 query + 1 active stream
更好?
答案 0 :(得分:3)
简短的回答是,您可能会遇到操作系统和/或DBMS的限制。
更长的答案取决于具体的操作系统和DBMS,但这里有几点需要考虑:
除了这些限制之外,如果您的代码是书面的,您将无法获得任何好处,因为它会按顺序运行连接。如果您要使用多个线程进行读取,则可能会看到具有多个并发连接的一些好处。但是,我仍然会根据需要打开这些连接。并且,为了避免为每个连接产生一个线程(并且遇到线程数的物理限制),在达到任何物理限制之前,您可能会达到实际的吞吐量限制。
答案 1 :(得分:0)
我在PostgreSQL中测试过,它可以工作。
由于PostgreSQL似乎没有预定义的最大游标限制,我仍然不知道从BLOB字段到Java InputStream
对象的游标/指针的简单分配通过java.sql.ResultSet.getBinaryStream("blob_field")
被认为是 主动检索操作 (我猜不是,但谁知道......);
使用类似InputStream
的内容一次加载所有SELECT blob_field FROM table WHERE length(blob_field)>0
,产生了非常长的查询执行时间,并且可以非常快速地访问二进制内容(按顺序方式,如上所述)。
测试用例为200 MB,每个文件包含20个10 MB的文件:
旧方法每个查询大约1秒钟,其他操作加上0.XX秒(读取每个InputStream并将其写入输出流,等等); 总累计时间:35秒
大型查询的实验方式大约需要22秒,迭代和执行其他操作需要12秒。 总累计时间:34秒
这让我认为在将BinaryStream从数据库分配给Java InputStream对象时,已经在执行完整的读取:/使用类似于byte []的InputStream(在这种情况下,最糟糕的是,因为实例化了所有项目导致的内存过载;
结论
一次读取所有内容的速度要快一些(执行每30秒快〜1秒), 但它可能严重地使大查询超时,除了导致RAM内存泄漏,并可能导致最大光标命中。不要在家里试试,只需坚持一次查询......