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

Gra*_*ray 28 java spring hibernate database-performance spring-transactions

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

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

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

要么

<!-- faster -->
<tx:method name="find*" read-only="false" propagation="REQUIRED" />
...
// faster
@Transaction
Run Code Online (Sandbox Code Playgroud)

与:

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

要么

<!-- faster -->
<tx:method name="find*" read-only="false" propagation="REQUIRED" />
...
// faster
@Transaction
Run Code Online (Sandbox Code Playgroud)

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

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

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

非常感谢.

Gra*_*ray 31

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

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

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

对问题#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
    
    Run Code Online (Sandbox Code Playgroud)
  2. 仔细观察tcpdump输出(tcpdump ... -X对于win),我注意到有很多不必要的自动提交和只读/读写JDBC调用.升级到我们使用的更好版本的真棒HikariCP连接池库有助于此.在版本2.4.1中,他们添加了一些智能,减少了一些这些调用.只读事务开销低至120ms.读写仍然是100ms.尼斯.

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

    jdbc:mysql://.../database?useLocalSessionState=true&useLocalTransactionState=true
    
    Run Code Online (Sandbox Code Playgroud)

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

    编辑/警告:useLocalTransactionState=true在发现驱动程序未发送交易信息的错误后,我们实际上回滚添加.

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

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

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

希望我的经验可以帮助他人