一尘不染

为什么Spring / Hibernate只读数据库事务运行得比读写慢?

hibernate

我一直在研究只读数据库和读写数据库事务的性能。MySQL服务器通过缓慢的VPN链接处于远程状态,因此我很容易看到事务类型之间的差异。这是与连接池有关的,我知道它是基于比较第一个和第二个JDBC调用而工作的。

当我将Spring AOP配置为在我的DAO调用上使用只读事务时,与读写相比,调用的 速度要慢 30-40%:

<!-- slower -->
<tx:method name="find*" read-only="true" propagation="REQUIRED" />
...
// slower
@Transaction(readOnly = true)

与:

<!-- faster -->
<tx:method name="find*" read-only="false" propagation="REQUIRED" />
...
// faster
@Transaction

看一下tcpdump,似乎只读事务正在与MySQL进行来回通信。这是只读转储读写

  1. 任何人都可以解释为什么只读呼叫需要更长的时间。这是预期的吗?

  2. 除了改善网络之外,我在做任何错事还是可以做些什么来提高他们的速度?刚刚发现这篇很棒的帖子,并提供了一些不错的性能建议。还有其他意见吗?

非常感谢。


阅读 489

收藏
2020-06-20

共1个答案

一尘不染

为什么Spring / Hibernate只读数据库事务运行得比读写慢?

好的,这是一个有趣的旅程。很多让我学习和分享。以下某些内容应该很明显,但希望我的无知以及我所学到的知识将对其他人有所帮助。

对问题1的简短回答是,hibernate@Transaction(readOnly = true)通过一个set session.transaction.read.only同步JDBC调用开始一个会话,然后以一个set session.transaction.read.write调用结束。在进行读写呼叫时不会发送这些呼叫,这就是只读呼叫较慢的原因。

对问题2的更长答案涉及以下为提高远程数据库性能而采取的步骤的详细信息:

  1. 我们要做的第一件事是在阅读此OpenVPN优化页面之后将数据库VPN从TCP切换到UDP 。叹。我应该知道这一点。我还将以下设置添加到OpenVPN客户端和服务器配置中。只读事务开销从480ms减少到141ms,但仍比读写的100ms多。大赢

    ; Got these from: https://community.openvpn.net/openvpn/wiki/Gigabit_Networks_Linux
    

    proto udp
    tun-mtu 6000
    fragment 0
    mssfix 0

  2. 在仔细查看tcpdump输出(tcpdump ... -X为了获胜)时,我注意到进行了许多不必要的自动提交和只读/读写JDBC调用。升级到我们使用的令人敬畏的HikariCP连接池库的新版本可以帮助解决这一问题。在2.4.1版中,他们增加了一些智能,从而减少了其中的一些调用。只读事务开销低至120ms。仍以100ms读写。真好

  3. HikariCP的作者Brett Wooldridge向我指出了可能会有所帮助的MySQL驱动程序设置。非常感谢老兄。在我们的MySQL JDBC URL中添加以下设置,可以告诉驱动程序使用连接的软件状态,而不向服务器询问状态。

    jdbc:mysql://.../database?useLocalSessionState=true&useLocalTransactionState=true
    

这些设置导致删除了更多同步JDBC命令。只读事务开销已降至60ms,现在与读写相同。呜呜

编辑/警告:useLocalTransactionState=true发现驱动程序未发送事务信息的错误后,我们实际上回滚了添加操作。

  1. 但是在更多地看tcpdump输出时,我仍然看到正在发送只读/读写事务设置。我的最后一个解决方法是编写一个自定义的只读检测池,如果它看到对连接的第一个调用是,则会从一个特殊池发出连接connection.setReadOnly(true)

使用此自定义池可将只读和读写连接的事务开销降低到20ms。我认为它基本上消除了最后的JDBC事务开销调用。这是我从主页编写的两个类的源代码。该代码相对来说比较脆弱,并且依靠Hibernate来做connection.setReadOnly(true)第一件事,但是它似乎运行良好,我在XML和代码中仔细地记录了下来。

因此@Transaction,经过几天的工作,基本开销从480ms变为了20ms。对一个dao.find(...)方法的100个“现实生活”hibernate调用始于55秒,结束于4.5秒。漂亮的屁股。希望总是很容易将速度提高10倍。

希望我的经验能对其他人有所帮助。

2020-06-20