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

时间:2016-01-14 18:52:51

标签: java spring hibernate database-performance spring-transactions

我一直在围绕只读与读写数据库事务的性能进行一些研究。 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交谈时做得更多。这是read-only dumpread-write的对比。

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

  2. 除了改善网络外,还有什么我做错了或能做些什么来提高速度?刚刚发现这篇精彩的帖子有一些good performance recommendations。还有其他意见吗?

  3. 非常感谢。

1 个答案:

答案 0 :(得分:29)

  

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

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

&LT; tldr&GT;对问题#1的简短回答是,hibernate从@Transaction(readOnly = true)会话开始,具有set session.transaction.read.only同步JDBC调用,并以set session.transaction.read.write调用结束。执行读写调用时不会发送这些调用,这就是为什么只读调用较慢的原因。 &LT; / tldr&GT;

问题#2的答案较长,涉及我尝试降低远程数据库性能的步骤的以下详细信息:

  1. 我们做的第一件事是在阅读OpenVPN optimization page后将数据库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 connection pool库的更新版本对此有所帮助。在版本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),它会从特殊池中发出连接。

    使用此自定义池会将只读和读写连接的事务开销降低到20毫秒。我认为它基本上删除了最后一个JDBC事务开销调用。这是我的主页two classes that I wrote的来源,写下了所有这些内容。代码相对脆弱,并依赖于Hibernate首先执行connection.setReadOnly(true)但它似乎运行良好,我在XML和代码中仔细记录它。

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

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