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

Why do spring/hibernate read-only database transactions run slower than read-write?

我一直在研究只读和读写数据库事务的性能。 MySQL 服务器通过慢速 VPN link 处于远程状态,因此我很容易看出交易类型之间的差异。这是连接池,我知道它是基于比较第一次和第二次 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 dump versus read-write.

  1. 谁能解释为什么只读调用花费的时间更长?这是预期的吗?

  2. 除了改善网络之外,我有什么地方做错了或者我可以做些什么来提高他们的速度吗?刚刚发现这个很棒的 post 和一些 good performance recommendations。还有其他意见吗?

非常感谢。

Why do spring/hibernate read-only database transactions run slower than read-write?

对问题 #1 的简短回答是,休眠以 set session.transaction.read.only 同步 JDBC 调用开始 @Transaction(readOnly = true) 会话,并以 set session.transaction.read.write 结束称呼。在执行 read-write 调用时不会发送这些调用,这就是 read-only 调用速度较慢的原因。请参阅下文,了解我对此的补救措施。

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

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

  1. 阅读本文后,我们做的第一件事是将数据库 VPN 从 TCP 切换到 UDP OpenVPN optimization page。叹。我应该知道这件事。我还在 OpenVPN 客户端和服务器配置中添加了以下设置。 Read-only 事务开销从 480 毫秒下降到 141 毫秒,但仍超过 read-write 的 100 毫秒。大赢。

    ; 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 the win)时,我注意到有很多不必要的 auto-commit 和 read-only/read-write JDBC 正在拨打电话。升级到我们使用的令人敬畏的 HikariCP connection pool 库的更新版本有助于解决这个问题。在 2.4.1 版中,他们添加了一些智能,减少了其中一些调用。 Read-only 事务开销降至 120 毫秒。 Read-write 仍为 100 毫秒。不错

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

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

    这些设置导致更多同步 JDBC 命令被删除。 Read-only 事务开销下降到 60 毫秒,现在与 read-write 相同。呜呜呜。

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

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

    ,它会从一个特殊的池中发出连接。

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

如此基本的 @Transaction 开销在几天的工作中从 480 毫秒减少到 20 毫秒。对 dao.find(...) 方法的 100 个“现实生活”休眠调用从 55 秒开始到 4.5 秒结束。漂亮的踢屁股。希望速度提高 10 倍总是这么容易。

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