为什么 spring/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. 除了改善网络之外,我做错了什么,或者我可以做些什么来提高他们的速度?刚刚发现这个很棒的帖子,有一些不错的性能建议。还有其他意见吗?

非常感谢。


答案 1

为什么 spring/hibernate 只读数据库事务的运行速度比读写慢?

<tldr>问题#1的简短答案是,休眠从会话开始,以同步JDBC调用结束。执行读写调用时不会发送这些调用,这就是只读调用速度较慢的原因。请参阅下面的补救措施。</tldr>@Transaction(readOnly = true)set session.transaction.read.onlyset session.transaction.read.write

好吧,这是一个有趣的旅程。很多东西让我学习和分享。以下一些应该是显而易见的,但希望我的无知和我学到的东西会对其他人有所帮助。

对问题 2 的较长回答涉及以下详细信息,这些步骤包括我为尝试提高远程数据库性能而采取的步骤:

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

    ; Got these from:
    ; https://community.openvpn.net/openvpn/wiki/Gigabit_Networks_Linux
    proto udp
    tun-mtu 6000
    fragment 0
    mssfix 0
    
  2. 在仔细查看 tcpdump 输出(获胜)时,我注意到有很多不必要的自动提交和只读/读写 JDBC 调用。升级到我们使用的令人敬畏的HikariCP连接池库的较新版本有助于解决这个问题。在2.4.1版本中,他们添加了一些智能,减少了其中一些调用。只读事务开销低至 120 毫秒。读写仍然在100ms。很好。tcpdump ... -X

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

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

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

    编辑/警告:在发现驱动程序未发送事务信息的错误后,我们实际上回滚了添加。不确定错误是在驱动程序中,休眠中还是在我们的代码中。useLocalTransactionState=true

  4. 但是在进一步查看 tcpdump 输出时,我仍然看到正在发送只读/读写事务设置。我的最后一个修复是编写一个自定义只读检测池,如果它看到对连接的第一次调用是 ,则从特殊池中发出连接。connection.setReadOnly(true)

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

因此,在几天的工作中,基本开销从480ms增加到20ms。对方法的 100 次“现实生活”休眠调用从 55 秒开始,到 4.5 秒结束。漂亮的踢屁股。希望获得10倍的速度提升总是如此容易。@Transactiondao.find(...)

希望我的经验能帮助别人。


答案 2

推荐