您不能直接从服务器 ping 客户端,但有一些选项。
关于超时
我认为在任何情况下你都必须使用服务器端超时 (wait_timeout),这个超时可以设置在服务器范围内或在从客户端连接时设置,例如:
SET SESSION wait_timeout = 60
这是必需的,因为客户端可能会在没有关闭 tcp 连接的情况下突然消失,并且显式超时将有助于 mysql 在客户端不活动的 wait_timeout 秒后关闭连接并释放资源。根据mysql manual默认超时时间相当大:28800秒。
有一个缺点。如果在正常操作期间您的客户端可能处于非活动状态超过 wait_timeout 秒,那么客户端应该知道如何处理关闭的连接(当数据库告诉它已经消失时重新连接)或者它应该发送“ping”查询(如select 1)至少每wait_timeout - 1 秒一次。
使用 get_lock() 函数
自 mysql 5.7(以及自 10.0.2 以来的 mariadb)您可以使用多个 user-level locks。
使用 GET_LOCK() 获得的锁通过执行 RELEASE_LOCK() 显式释放或在会话终止(正常或异常)时隐式释放。事务提交或回滚时不会释放使用 GET_LOCK() 获得的锁。
所以想法是在客户端连接时发出 get_lock 查询,例如:
SELECT GET_LOCK('logged_in_{CLIENT_ID}', timeout)
您可以将 timeout 设置为 0 并立即告诉客户端无法登录,或者您可以等待(阻塞)最多 wait_timeout 秒以确保确实有另一个客户端持有锁。
当客户端断开连接或等待超时后,服务器将自动释放此锁。
如果锁是免费的 get_lock() 将返回1 否则(等待超时秒后)将返回0
使用进程ID
如果不想使用锁,可以使用进程的PID。
当客户端连接而不是写入islogged = 'YES' 时,您可以使用当前的 CONNECTION_ID() 作为值。
在登录之前,您可以检查当前客户端没有像这样的活动进程
SELECT islogged FROM logged
INNER JOIN information_schema.processlist ON
processlist.id = logged.islogged
WHERE
client_id = ...
如果上面的查询没有返回任何内容,那么您可以将新的 pid 插入到已记录的表中
REPLACE INTO logged SET islogged = CONNECTION_ID(), client_id = ...
我更喜欢使用 get_lock(),因为它看起来更容易,不会遇到并发问题并且允许实现等待。
不要忘记超时是必不可少的,您必须处理重新连接或发送定期 ping 以避免客户端出现意外的“服务器已消失”问题。