为什么spring / hibernate只读数据库事务比read-write运行得慢?

我一直在研究只读与读写数据库事务的性能。 MySQL服务器是远程的,因此我很容易看到不同事务类型之间的差异。 这是连接池,我知道它基于比较第一个和第二个JDBC调用。

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

  

要么

 // slower @Transaction(readOnly = true) 

与:

   

要么

 // faster @Transaction 

看看tcpdump,似乎只读事务在与MySQL交谈时做得更多。 这是只读转储与读写 。

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

  2. 除了改进网络之外,还有什么我做错了或我可以做些什么来提高速度? 刚刚发现了这篇很棒的post并提供了一些很好的性能建议 。 还有其他意见吗?

非常感谢。

为什么spring / hibernate只读数据库事务比read-write运行得慢?

好的,这是一个有趣的旅程。 很多我可以学习和分享。 下面的一些应该是显而易见的,但希望我的无知和我所学到的将有助于其他人。

对问题#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 for win),我注意到有很多不必要的自动提交和只读/读写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

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

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

因此,基本的@Transaction开销在几天的工作中从480毫秒增加到20毫秒。 100个“真实生活”hibernate对dao.find(...)方法的调用从55秒开始,以4.5秒结束。 漂亮踢屁股。 希望速度提高10倍总是很容易。

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