我正在尝试按照此博客上的说明使用 Spring Data JPA 流。但是,我无法通过任何日志监控该过程或进度。当进程尝试批量提取数据时,我是否应该看到日志中打印多个 SQL 查询?如果不是,那么我怎么知道所有行都没有一次性加载?
其他博客,例如这个blog和this,建议我应该将MySQL的
HINT_FETCH_SIZE
设置为Integer.MIN_VALUE
,我认为这可能是解决方案,但这会引发以下异常:
2024-01-29 14:40:20.843 警告 78247 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper:SQL 错误:0,SQLState:S1000 2024-01-29 14:40:20.843错误78247 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper:流式传输结果集com.mysql.cj.protocol.a.result.ResultsetRowsStreaming@ 4ca63fa5 仍然活跃。当任何流结果集在给定连接上打开并使用时,不得发出任何语句。在尝试更多查询之前,请确保您已对任何活动的流结果集调用 .close()。 结束时间:48 org.springframework.orm.jpa.JpaSystemException:无法提取结果集;嵌套异常是 org.hibernate.exception.GenericJDBCException:无法提取 ResultSet 在org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:331)
这是我的存储库代码:
@QueryHints(value = {
@QueryHint(name = org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE, value = "" + Integer.MIN_VALUE),
@QueryHint(name = org.hibernate.jpa.QueryHints.HINT_CACHEABLE, value = "false"),
@QueryHint(name = org.hibernate.jpa.QueryHints.HINT_READONLY, value = "true"),
})
@Query("SELECT s FROM Salary s")
Stream<Salary> findAllStream();
我想我想得到一个保证,如果以上是在 Spring Data JPA 中使用流查询的正确方法,因为我自己无法可靠地监控流的性能?
更新
上面的异常是由于在同一调用方法中重复调用 findAllStream 方法而发生的。删除其中一个修复了异常。
我找不到任何日志配置来显示数据是否正在批量拉入。但我确实找到了一种在本地测试性能的方法。
为了测试流媒体功能,我需要访问包含数百万条记录的数据库。我使用 Docker 映像来使用 MySQL 员工数据https://hub.docker.com/r/genschsa/mysql-employees
设置 docker 映像后,我在将 MySQL Workbench 与服务器连接时遇到问题。看起来 docker 映像未配置为接受默认设置的 SSL 连接。我必须禁用
Use SSL
标志才能建立连接。此设置出现在 MySQL 工作台的 SSL 选项卡下。
应用程序中的连接字符串也必须这样配置:
spring.datasource.url=jdbc:mysql://localhost:3307/employees?verifyServerCertificate=false&useSSL=false&requireSSL=false
employees DB 中的数据包含一个名为
Salaries
的表,该表约有 280 万行。
为了测试,我编写了一个小型 Spring Data JPA 应用程序,该应用程序在存储库类中具有以下方法和一个简单的控制器来调用这些方法:
@Override
List<Salary> findAll();
@QueryHints(value = {
@QueryHint(name = org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE, value = "" + Integer.MIN_VALUE),
@QueryHint(name = org.hibernate.jpa.QueryHints.HINT_CACHEABLE, value = "false"),
@QueryHint(name = org.hibernate.jpa.QueryHints.HINT_READONLY, value = "true"),
})
@Query("SELECT s FROM Salary s")
Stream<Salary> findAllStream();
然后我编写了一小段代码,将读取的数据转换为 json 对象,然后使用多个线程写回到文件中。这是为了模拟现实案例中的处理。
这是我观察到的。
使用List方法时,内存使用量显着增加。最初的查询花费了大部分时间,但是当加载所有数据后,实际的数据处理任务很快就完成了。
使用Stream方法时,对内存使用的影响几乎不明显。但总体而言,与 List 方法相比,完成处理部分的性能相似或更差。
结论
我的上述发现使我得出结论,仅当存在内存不足的风险(即获取
Stream
)时,才应使用存储库方法的 out of memory exception
返回类型。否则,如果您的应用程序已经在足够大的服务器上运行,那么对内存使用的总体影响几乎不会被注意到,并且只有在您的进程快速完成时才会是暂时的。
来自 IntelliJ Profiler 的内存使用统计数据