2

在编写解决下载具有低 RAM 影响的巨大动态 zip问题的解决方案时,一个想法开始困扰我,并导致了这个问题,要求纯粹的好奇心/对知识的渴望:

如果不是InputStream一次加载一个 s (对数据库进行单独的查询),我会InputStream在一个查询中加载所有 s ,返回一个列表(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?

4

2 回答 2

3

简短的回答是,您可能会遇到操作系统和/或 DBMS 的限制。

更长的答案取决于特定的操作系统和 DBMS,但这里有一些事情需要考虑:

  • 在 Linux 上,任何进程都可以拥有最大数量的打开文件描述符。默认值是/曾经是 1024,但相对容易增加。这个限制 IMO 的目的是杀死一个写得不好的进程,因为每个文件/套接字所需的内存量是最小的(在现代机器上)。
  • 如果打开的流表示与数据库的单个套接字连接,则单个机器可以打开到单个服务器地址/端口的客户端套接字总数存在硬性限制。这是由客户端的动态端口地址范围驱动的,它可以是 16 或 32k(但可以修改)。此限制适用于机器上的所有进程,因此一个进程的过度消耗可能会使尝试访问同一服务器的其他进程饿死。
  • 根据 DBMS 管理用于检索 BLOB 的连接的方式,您可能会遇到 DBMS 强制执行的小得多的限制。例如,Oracle 默认每个用户连接总共有 50 个“游标”(主动检索操作)。

除了这些限制之外,鉴于您编写的代码,您不会获得任何好处,因为它按顺序通过连接运行。如果您要使用多个线程来读取,您可能会看到拥有多个并发连接的一些好处。但是,我仍然会根据需要打开这些连接。并且为了避免您考虑为每个连接生成一个线程(并遇到线程数的物理限制),您可能会在达到任何物理限制之前达到实际的吞吐量限制。

于 2013-05-24T16:39:06.623 回答
0

我在 PostgreSQL 中对其进行了测试,并且可以正常工作。

由于 PostgreSQL 似乎没有预定义的最大游标限制,我仍然不知道从 BLOB 字段到 JavaInputStream对象的游标/指针的简单分配java.sql.ResultSet.getBinaryStream("blob_field")是否被认为是主动检索操作(我猜不是,但是谁知道...);

InputStream使用类似的东西一次加载所有s 会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 内存泄漏和可能的最大光标命中。

不要在家里尝试这个,一次只坚持一个查询......

于 2013-05-27T13:23:39.520 回答