【问题标题】:Pro and Cons of opening multiple InputStream?打开多个 InputStream 的利弊?
【发布时间】:2013-05-20 07:15:39
【问题描述】:

在将the solution 编码为downloading a huge dynamic zip with low RAM impact 的问题时,一个想法开始困扰我,并导致了这个问题,要求纯粹的好奇心/对知识的渴望:

如果不是一次加载一个InputStreams(对数据库有单独的查询),我会在一个查询中加载所有InputStreams,我会遇到什么样的缺点,返回一个列表(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 更好?

【问题讨论】:

  • Too many file opens 怎么样。
  • @Paul Vargas,这会很奇怪,因为这里不涉及文件 :)

标签: java database memory-management inputstream


【解决方案1】:

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

更长的答案取决于具体的操作系统和 DBMS,但这里有几点需要考虑:

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

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

【讨论】:

  • +1,很好的答案,谢谢。上述“假设版本”的好处在于单个查询而不是很多查询。由于查询需要几毫秒,避免数千次查询数据库可以节省几秒钟。关于50 "cursor (active retrieval operations),是讨论的核心;当我调用 rs.getBinaryStream("field") 时,我实际上是在 激活 一个光标,还是只是为了 惰性激活实例化它b> 稍后,当向它请求数据时?
【解决方案2】:

我在 PostgreSQL 中测试过,它可以工作。

由于 PostgreSQL 似乎没有预定义的 最大游标限制,我仍然不知道是否通过 @ 将 BLOB 字段中的游标/指针简单分配给 Java InputStream 对象987654322@ 是否被认为是主动检索操作(我猜不是,但谁知道...);

使用类似SELECT blob_field FROM table WHERE length(blob_field)&gt;0 的东西一次加载所有InputStreams,会产生非常长的查询执行时间,并且可以非常快速地访问二进制内容(以顺序方式,如上所述)。


使用 200 MB 的测试用例,包含 20 个文件,每个文件 10 MB:

  • 旧方法是每个查询大约 1 秒,加上其他操作 0.XX 秒(读取每个 InputStream 并将其写入输出流,等等); 总耗时:35 秒

  • 实验方式大查询大约需要 22 秒,迭代和执行其他操作总共需要 12 秒。 总耗时:34 秒

这让我想到,在将 BinaryStream 从数据库分配给 Java InputStream 对象时,已经执行了完整的读取:/ 使用类似于 byte[] 的 InputStream(但在这种情况下最糟糕的是,因为实例化所有项目会导致内存过载);


结论

一次读取要快一点(每执行 30 秒快 1 秒), 但它可能会严重导致大查询超时,除了导致 RAM 内存泄漏和可能的最大光标命中。

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

【讨论】:

    猜你喜欢
    • 2011-07-06
    • 2011-08-02
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 2017-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多