【问题标题】:Cheapest way to to determine if a MySQL connection is still alive确定 MySQL 连接是否仍然存在的最便宜的方法
【发布时间】:2011-02-02 13:30:35
【问题描述】:

我有一个用于基于 Web 的数据服务的 MySQL 连接池。当它开始为请求提供服务时,它会从池中获取一个连接以供使用。问题是,如果在使用该特定连接后出现了明显的暂停,则服务器可能已将其超时并关闭其结束。我希望能够在池管理代码中检测到这一点。

诀窍是:我正在编码的环境只为我提供了一个非常抽象的连接 API。我基本上只能执行SQL语句。我无权访问实际的套接字或直接访问 MySQL 客户端 API。

所以,问题是:我可以在连接上执行以确定它是否正常工作的最便宜的 MySQL 语句是什么。例如SELECT 1; 应该可以,但我想知道是否有更便宜的东西?也许某些东西甚至没有通过网络,而是在 MySQL 客户端库中处理并有效地回答了相同的问题?

澄清:我不关心检查 MySQL 服务器是否正在运行,或者它的数据库配置是否足以回答查询。如果这些事情发生了,那么服务执行的后续 SQL 将获取并处理相应的错误。我只关心 TCP 连接是否打开......因为如果服务器关闭它,那么 Web 服务的 SQL 将收到一个错误,表示“只需重新连接并重试”,一旦关闭,这样做会很不方便服务代码的垃圾。

关闭:/* ping */ hack 正是我正在寻找的那种东西,但是只能通过 JDBC 获得。阅读该 hack 的文档后,很明显它被放在那里是为了完全和我想要它的原因相同。出于好奇,我在Haskel 工作,使用HDBCHDBC-mysql。我将要求 HDBC-mysql 的作者添加一种直接或通过类似 hack 调用 mysql_ping() 的方法。

Vlad 的DO 1 也是我所追求的,而且由于其他 hack 在 JDBC 之外不可用,我将使用它。

感谢所有精彩的讨论,尤其是@Vlad!

【问题讨论】:

  • 如果没有任何请求返回,是否有可能确定服务器是否正在运行?

标签: mysql libmysql hdbc


【解决方案1】:

我不确定您当前使用的是什么 API(或什么语言),但是对于 Java,JDBC 驱动程序可以做一个特殊的技巧。

标准测试查询是:

select 1

正如您所指出的。如果修改为:

/* ping */ select 1

JDBC 驱动程序会注意到这一点,并且只向 MySQL 服务器发送一个数据包以获得响应。

我在名为 MySQL Tips for Java Developers With Mark Matthews 的 Sun 'Deep Dive' 剧集中了解到这一点。

即使您不使用 Java,也许其他 mysql 驱动程序已经实现了相同的技巧?我假设服务器需要知道这个特殊的数据包,以便它可以发送响应......

【讨论】:

  • 这正是我一直在寻找的东西。唉,它只能通过 JDBC 驱动程序使用。 (我去查看了源代码:它是在 jdbc/StatementImpl.java 中的 Connector/J 代码中实现的,而不是在任何其他 Connector 或客户端库中。)
  • @Joshua、mysql_pingSELECT 1DO 1 实际上导致通过线路(或套接字)交换完全相同数量的 TCP 数据包,唯一的区别在于解析( mysql_ping 不需要语句解析。)
【解决方案2】:

不经过线路就不会知道连接的真实状态SELECT 1 是一个足够好的候选者(可以说你可以想出一个更短的命令,花费更少的时间解析,但与网络甚至环回延迟相比,这些节省是微不足道的。)

话虽如此,我认为在从池中检查连接之前ping 连接并不是最好的方法

您可能应该简单地让您的连接池管理器强制执行其自己的保持活动(超时)策略以避免被服务器断开连接(没有更严重的干预连接问题,这可能会影响您无论如何都要在常规操作中进行操作——而且您的连接池管理器无论如何都无法提供帮助),以及为了不占用数据库(想想文件句柄和内存使用) .

因此,在我看来,在从池中检查连接之前测试连接条件的真正价值是值得怀疑的。 在将连接检入回池之前可能值得测试连接状态,但这可以通过在出现 SQL 硬错误(或等效异常)时简单地将连接标记为脏来隐式完成(除非您使用的 API 已经为您公开了类似 is-bad 的调用。)

因此我建议:

  • 实施客户端保活策略
  • 从池中检出连接时不执行任何检查
  • 在连接返回池之前执行脏检查
  • 让应用程序代码处理其他(非超时)异常连接条件

更新

从您的 cmets 看来,您真的真的想要 ping 连接(我认为这是因为您没有完全控制或了解 MySQL 服务器上的超时特征或介入网络设备,例如代理等)

在这种情况下,您可以使用DO 1 作为SELECT 1 的替代品;它略微更快——解析时间更短,并且它不返回实际数据(尽管您获得 TCP acks,因此您仍将进行往返验证连接是否仍然建立。)

更新 2

关于Joshua's post,以下是各种场景的抓包跟踪:

SELECT 1;
13:51:01.463112 IP client.45893 > server.mysql: P 2270604498:2270604511(13) ack 2531191393 win 1460 <nop,nop,timestamp 2983462950 59680547>
13:51:01.463682 IP server.mysql > client.45893: P 1:57(56) ack 13 win 65306 <nop,nop,timestamp 59680938 2983462950>
13:51:01.463698 IP client.45893 > server.mysql: . ack 57 win 1460 <nop,nop,timestamp 2983462951 59680938>

DO 1;
13:51:27.415520 IP client.45893 > server.mysql: P 13:22(9) ack 57 win 1460 <nop,nop,timestamp 2983488906 59680938>
13:51:27.415931 IP server.mysql > client.45893: P 57:68(11) ack 22 win 65297 <nop,nop,timestamp 59681197 2983488906>
13:51:27.415948 IP client.45893 > server.mysql: . ack 68 win 1460 <nop,nop,timestamp 2983488907 59681197>

mysql_ping
14:54:05.545860 IP client.46156 > server.mysql: P 69:74(5) ack 78 win 1460 <nop,nop,timestamp 2987247459 59718745>
14:54:05.546076 IP server.mysql > client.46156: P 78:89(11) ack 74 win 65462 <nop,nop,timestamp 59718776 2987247459>
14:54:05.546092 IP client.46156 > server.mysql: . ack 89 win 1460 <nop,nop,timestamp 2987247459 59718776>

如您所见,除了 mysql_ping 数据包是 5 个字节而不是 DO 1; 的 9 个字节这一事实之外,往返次数(因此,网络引起的延迟)完全相同。与mysql_ping 相比,您使用DO 1 支付的唯一额外费用是解析DO 1,这是微不足道的。

【讨论】:

  • 即使池有自己的超时时间(确实如此),服务器也可以选择在超时之前关闭套接字。然后池有一个未打开的套接字。我还已经处理了关闭导致硬 SQL 错误而不是返回它们的套接字的问题,因此池将在下次需要连接时重新连接。当一个良好的连接被放回池中时,问题就出现了,然后,在等待被重用时(在池的超时内),服务器关闭了套接字。
  • 服务器何时以及为什么会选择任意关闭该套接字?服务器超时是不确定的,还是您想要一种通用机制,即使(错误地)将 keep-alive 设置为大于服务器(或中间代理等)空闲连接超时也能正常工作?
  • 是的,后者——我想要一个通用的解决方案。此代码将用于具有许多不同配置的非常大的部署 - 最好是针对不合理的配置进行防御。
  • 啊哈 - 错过了您的 DO 1 建议。我认为这是我目前能做的最好的事情。感谢您的所有意见和讨论!
【解决方案3】:

这种情况下的“连接”有多种含义。 MySQL 侦听套接字——这是网络级别的“连接”。 MySQL 维护“数据库连接”,其中包括查询执行的上下文和其他开销。

如果你只是想知道服务是否正在监听,你应该能够执行网络级调用来查看端口(不知道默认是什么)是否正在监听目标 IP。如果您想让 MySQL 引擎响应,我认为您的 SELECT 1 想法很好 - 它实际上并没有从数据库中获取任何数据,但确实确认引擎已启动并响应。

【讨论】:

  • 我正在寻找测试套接字是否仍然打开。如果另一端的服务器宕机,那么应用程序级别的故障是可以的。如果服务器已启动,但套接字已关闭,我想在将连接交给应用程序之前捕获它,因为它无法轻松重试(叹气!)。
猜你喜欢
  • 1970-01-01
  • 2018-02-21
  • 2011-11-29
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-27
相关资源
最近更新 更多