【问题标题】:Does MySQL Connector/J buffer rows when "streaming" a ResultSet?MySQL Connector/J 在“流式传输”ResultSet 时是否缓冲行?
【发布时间】:2016-05-22 15:16:16
【问题描述】:

根据我的阅读,我看到使用 MySQL JDBC 驱动程序在 MySQL 中流式传输 ResultSet 的方法是这两个命令:

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);

我的问题是专家能否澄清使用上述代码流式传输 ResultSet 是否将一行返回给客户端,然后转到服务器以获取下一行等等(效率极低),或者它是否足够聪明,可以像BufferedStreamReader?如果是缓冲流,如何设置缓冲大小?

编辑:来自doc

只进、只读的结果集与 fetch 的组合 Integer.MIN_VALUE 的大小用作驱动程序流式传输的信号 结果集逐行。在此之后,使用 语句将逐行检索。

这是否意味着如果我有 1000 万行,那么有 1000 万次往返服务器来获取这些行?这是非常低效的。如何流式传输ResultSet 但将其缓冲以便我不必进行如此多的往返?

EDIT2:当 fetchSize 设置为 Integer.MIN_VALUE 时,MySQL 似乎会自动进行一些缓冲。在我的测试中,我能够使用setFetchSize(Integer.MIN_VALUE) 在不到 20 分钟的时间内读取超过 40M 行。这相当于每秒大约 30,000 行。我不知道平均行有多大,但很难想象每秒 30,000 次往返。

还有一个单独的问题:如果结果集的元素多于 fetchSize,MySQL 会做什么?例如,结果集有 10M 行,而 fetchSize 设置为 1000。那么会发生什么?

【问题讨论】:

标签: java mysql jdbc


【解决方案1】:

当 fetchSize 设置为 Integer.MIN_VALUE 时,MySQL 似乎会自动进行一些缓冲。

确实如此,至少有时如此。我使用 Wireshark 测试了 MySQL Connector/J 版本 5.1.37 的行为。对于表...

CREATE TABLE lorem (
    id INT AUTO_INCREMENT PRIMARY KEY,
    tag VARCHAR(7),
    text1 VARCHAR(255),
    text2 VARCHAR(255)
    )

...带有测试数据...

 id  tag      text1            text2
---  -------  ---------------  ---------------
  0  row_000  Lorem ipsum ...  Lorem ipsum ...
  1  row_001  Lorem ipsum ...  Lorem ipsum ...
  2  row_002  Lorem ipsum ...  Lorem ipsum ...
...
999  row_999  Lorem ipsum ...  Lorem ipsum ...

(where both `text1` and `text2` actually contain 255 characters in each row)

...和代码...

try (Statement s = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY)) {
    s.setFetchSize(Integer.MIN_VALUE);
    String sql = "SELECT * FROM lorem ORDER BY id";
    try (ResultSet rs = s.executeQuery(sql)) {

...在s.executeQuery(sql) 之后——即在rs.next() 甚至被调用之前——MySQL 连接器/J 已经从表中检索了前约 140 行。

实际上,当只查询tag 列时

    String sql = "SELECT tag FROM lorem ORDER BY id";

MySQL Connector/J 立即检索到所有 1000 行,如 Wireshark 网络帧列表所示:

将查询发送到服务器的第 19 帧如下所示:

MySQL 服务器以第 20 帧响应,该帧以 ...开头。

... 紧随其后的是第 21 帧,该帧以 ...开始

... 以此类推,直到服务器发送了第 32 帧,该帧以

结束

由于唯一的区别是每行返回的信息量,我们可以得出结论,MySQL Connector/J 根据每个返回的行的最大长度和可用的可用内存量来决定适当的缓冲区大小。

如果结果集的元素多于 fetchSize,MySQL 会做什么?例如,结果集有 10M 行,而 fetchSize 设置为 1000。那么会发生什么?

MySQL Connector/J 最初检索第一组fetchSize 行,然后当rs.next() 移动通过它们时,它最终将检索下一组行。即使setFetchSize(1) 也是如此,顺便说一句,这是真正一次只能获得一行的方式。

(请注意,对于 n>0,setFetchSize(n) 在连接 URL 中需要 useCursorFetch=truesetFetchSize(Integer.MIN_VALUE) 显然不需要。)

【讨论】:

  • 有趣的是,这似乎与dev.mysql.com/doc/connector-j/en/… 上的文档中描述的内容不符(他们甚至忽略了提及useCursorFetch)。这确实让我想知道useCursorFetch 是否与使用Integer.MIN_VALUE 有相同的注意事项。
  • 您能否分享有关您如何使用 Wireshark 测试行为的详细信息?
  • 请问这个深入研究/测试针对的是哪个版本的驱动程序?
  • @matanster 测试是使用 MySQL Connector/J 版本 5.1.37 执行的。
猜你喜欢
  • 1970-01-01
  • 2016-06-27
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 2012-07-12
  • 2015-10-26
  • 1970-01-01
  • 2013-08-13
相关资源
最近更新 更多