MySQL 消失了:Connection_errors_peer_address 的数字很高

Posted

技术标签:

【中文标题】MySQL 消失了:Connection_errors_peer_address 的数字很高【英文标题】:MySQL has gone away: Connection_errors_peer_address with high numbers 【发布时间】:2019-03-01 00:03:28 【问题描述】:

我们有 mysql 5.7 主从复制,在从服务器端,我们的应用程序监控工具(Tideways 和 php7.0)不时报告

MySQL 已经消失。

检查MYSQL端:

show global status like '%Connection%';

+-----------------------------------+----------+
| Variable_name                     | Value    |
+-----------------------------------+----------+
| Connection_errors_accept          | 0        |
| Connection_errors_internal        | 0        |
| Connection_errors_max_connections | 0        |
| Connection_errors_peer_address    | 323      |
| Connection_errors_select          | 0        |
| Connection_errors_tcpwrap         | 0        |
| Connections                       | 55210496 |
| Max_used_connections              | 387      |
| Slave_connections                 | 0        |
+-----------------------------------+----------+

Connection_errors_peer_address 显示 323。如何进一步调查导致双方出现此问题的原因:

MySQL 消失了

Connection_errors_peer_address

编辑:

主服务器

net_retry_count = 10 
net_read_timeout = 120 
net_write_timeout = 120 
skip_networking = OFF
Aborted_clients = 151650

从服务器 1

net_retry_count = 10
net_read_timeout = 30 
net_write_timeout = 60 
skip_networking = OFF
Aborted_clients = 3

从服务器 2

net_retry_count = 10
net_read_timeout = 30 
net_write_timeout = 60 
skip_networking = OFF
Aborted_clients = 3

【问题讨论】:

主节点my.cnf中wait_timeoutmax_allowed_packet的值是多少? 这可能是由于服务器超时。要解决此问题,请检查配置文件中的 wait_timeout mysql 变量是否足够。 @Alexey: wait_timeout = 600max_allowed_packet = 100M 【参考方案1】:

在 MySQL 5.7 中,当一个新的 TCP/IP 连接到达服务器时,服务器会执行多项检查,在函数 check_connection() 中的 sql/sql_connect.cc 中实现

其中一项检查是获取客户端连接的 IP 地址,如下所示:

static int check_connection(THD *thd)

...
  if (!thd->m_main_security_ctx.host().length)     // If TCP/IP connection
  
...
    peer_rc= vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST);
    if (peer_rc)
    
      /*
        Since we can not even get the peer IP address,
        there is nothing to show in the host_cache,
        so increment the global status variable for peer address errors.
      */
      connection_errors_peer_addr++;
      my_error(ER_BAD_HOST_ERROR, MYF(0));
      return 1;
    
...

失败时,状态变量connection_errors_peer_addr 递增,连接被拒绝。

vio_peer_addr()vio/viosocket.c 中实现(代码简化为仅显示重要调用)

my_bool vio_peer_addr(Vio *vio, char *ip_buffer, uint16 *port,
                      size_t ip_buffer_size)

  if (vio->localhost)
  
...
  
  else
  
    /* Get sockaddr by socked fd. */

    err_code= mysql_socket_getpeername(vio->mysql_socket, addr, &addr_length);

    if (err_code)
    
      DBUG_PRINT("exit", ("getpeername() gave error: %d", socket_errno));
      DBUG_RETURN(TRUE);
    

    /* Normalize IP address. */

    vio_get_normalized_ip(addr, addr_length,
                          (struct sockaddr *) &vio->remote, &vio->addrLen);

    /* Get IP address & port number. */

    err_code= vio_getnameinfo((struct sockaddr *) &vio->remote,
                              ip_buffer, ip_buffer_size,
                              port_buffer, NI_MAXSERV,
                              NI_NUMERICHOST | NI_NUMERICSERV);

    if (err_code)
    
      DBUG_PRINT("exit", ("getnameinfo() gave error: %s",
                          gai_strerror(err_code)));
      DBUG_RETURN(TRUE);
    
...
  
...

简而言之,vio_peer_addr() 中唯一的失败路径发生在对 mysql_socket_getpeername()vio_getnameinfo() 的调用失败时。

mysql_socket_getpeername() 只是 getpeername() 之上的一个包装器。

man 2 getpeername 手册列出了以下可能的错误:

名字

   getpeername - get name of connected peer socket

错误

   EBADF  The argument sockfd is not a valid descriptor.

   EFAULT The addr argument points to memory not in a valid part of the process address space.

   EINVAL addrlen is invalid (e.g., is negative).

   ENOBUFS
          Insufficient resources were available in the system to perform the operation.

   ENOTCONN
          The socket is not connected.

   ENOTSOCK
          The argument sockfd is a file, not a socket.

在这些错误中,只有ENOBUFS 是合理的。

至于vio_getnameinfo(),它只是getnameinfo() 的一个包装器,同样根据手册页man 3 getnameinfo 可能会失败,原因如下:

名字

   getnameinfo - address-to-name translation in protocol-independent manner

返回值

   EAI_AGAIN
          The name could not be resolved at this time.  Try again later.

   EAI_BADFLAGS
          The flags argument has an invalid value.

   EAI_FAIL
          A nonrecoverable error occurred.

   EAI_FAMILY
          The address family was not recognized, or the address length was invalid for the specified family.

   EAI_MEMORY
          Out of memory.

   EAI_NONAME
          The name does not resolve for the supplied arguments.  NI_NAMEREQD is set and the host's name cannot be located, or neither 

主机名或服务名 被请求了。

   EAI_OVERFLOW
          The buffer pointed to by host or serv was too small.

   EAI_SYSTEM
          A system error occurred.  The error code can be found in errno.

   The gai_strerror(3) function translates these error codes to a human readable string, suitable for error reporting.

这里可能会发生很多故障,主要是由于负载过重或网络造成的。

要了解这段代码背后的过程,MySQL服务器本质上是在做一个Reverse DNS lookup,来:

找到客户端的主机名 找到这个主机名对应的IP地址 稍后再将此 IP 地址转换为主机名(请参阅随后对 ip_to_hostname() 的调用)。

总体而言,Connection_errors_peer_address 造成的故障可能是由于系统负载(导致内存不足等暂时性故障)或影响 DNS 的网络问题。

披露:我恰好是在 MySQL 中实现此 Connection_errors_peer_address 状态变量的人,作为在该代码区域具有更好可见性/可观察性的努力的一部分。

[编辑] 跟进更多细节和/或指南:

Connection_errors_peer_address 增加时,根本原因不会打印在日志中。这对于故障排除来说是不幸的,但也避免了洪水日志造成更大的损害,这里有一个权衡。请记住,登录之前发生的任何事情都非常敏感... 如果服务器真的内存不足,很可能很多其他的东西会坏掉,服务器会很快宕机。通过监视mysqld 的总内存使用量和监视uptime,应该相当容易确定故障是否“仅”导致连接关闭而服务器保持运行,或者服务器本身是否发生灾难性故障。 假设服务器在故障时保持正常运行,那么罪魁祸首更有可能是第二次调用getnameinfo。 使用skip-name-resolve 将不起作用,因为此检查稍后会发生(请参阅check_connection() 中的代码中的specialflag & SPECIAL_NO_RESOLVE) 当Connection_errors_peer_address失败时,注意服务器干净利落地返回错误ER_BAD_HOST_ERROR给客户端,然后关闭socket。这与突然关闭一个套接字(如崩溃)不同:前者应由客户端报告为"Can't get hostname for your address",而后者应报告为"MySQL has gone away"。 客户端连接器是否真正对待ER_BAD_HOST_ERROR 和关闭的套接字不同是另一回事

鉴于此故障总体上似乎与 DNS 查找有关,我将检查以下项目:

查看performance_schema.host_cache 表中有多少行。 将此与主机缓存的大小进行比较,请参阅host_cache_size 系统变量。 如果主机缓存已满,请考虑增加其大小:这将减少整体 DNS 调用的数量,减轻 DNS 的压力,希望(诚然,这只是在黑暗中的一个镜头)DNS 瞬时故障会消失. 5500 万个连接中的 323 个确实看起来是短暂的。假设监控客户端有时确实连接正确,请检查表 host_cache 中此客户端的行:它可能包含报告的其他个故障。

performance_schema.host_cache 文档:

https://dev.mysql.com/doc/refman/5.7/en/host-cache-table.html

进一步阅读:

http://marcalff.blogspot.com/2012/04/performance-schema-nailing-host-cache.html

[编辑 2] 基于可用的新数据:

Aborted_clients 状态变量显示一些连接被服务器强制关闭。这通常发生在会话空闲很长时间时。

发生这种情况的典型情况是:

    客户端打开连接,并发送一些查询 然后客户端在很长一段时间内什么都不做(大于 net_read_timeout) 由于流量不足,服务器关闭会话,并增加 Aborted_connects 然后客户端发送另一个查询,看到一个关闭的连接,并报告“MySQL 已消失”

请注意,忘记干净关闭会话的客户端应用程序将执行 1-3,这可能是主服务器上的 Aborted_clients 的情况。此处进行一些清理以修复使用 master 的客户端应用程序将有助于减少资源消耗,因为让 151650 个会话打开以在超时时终止是有代价的。

执行 1-4 的客户端应用程序可能会导致服务器上的 Aborted_clients 并且 MySQL 已在客户端上消失。报告“MySQL 已消失”的客户端应用程序很可能是这里的罪魁祸首。

例如,如果监控应用程序每 N 秒检查一次服务器,则确保超时(此处为 30 和 60 秒)明显大于 N,否则服务器将终止监控会话。

【讨论】:

skip-name-resolve 是否相关?如果是这样,它是一个“解决方案”吗? 非常感谢 Marc 的详细描述。 Connection_errors_peer_address 是我在 PHP 端获取 MySQL 已消失消息时试图调查的潜在信息来源。您提到Connection_errors_peer_address 可能是由于系统负载或网络问题影响 DNS。如何找出真正发生的事情。当Connection_errors_peer_address 增加时,MySQL 日志中是否存储了一些内容,以便我可以探索并找出真正发生的事情。 @RickJames,你的意思是用skip-name-resolve启动MySQL,这样check_connection函数就不会被触发,然后消除这个错误源? @Miloš - 我不确定;我猜。 (因此,我没有将其作为“答案”呈现。 罗杰,谢谢。我更期待从@MarcAlff 那里得到对该评论的答复。

以上是关于MySQL 消失了:Connection_errors_peer_address 的数字很高的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 服务器在 Drupal 7 中消失了

linux下修改MySQL root密码后数据库消失

MySQL 消失了:Connection_errors_peer_address 的数字很高

MySQL 服务器在 XAMPP 中消失了

#2006 MySQL 服务器在 Wamp 中消失了错误

重新登录到 phpmyadmin 后,存储过程在 mysql 中消失了