MySQL消失了:Connection_errors_peer_address的编号很高

时间:2018-09-25 11:16:40

标签: mysql ubuntu mysql-5.7

我们拥有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

1 个答案:

答案 0 :(得分:7)

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

其中一项检查是获取客户端连接的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手册列出了以下可能的错误:

  

NAME

   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,它也可能由于以下原因而失败:

  

NAME

   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无效,因为稍后会进行此检查(请参见specialflag & SPECIAL_NO_RESOLVE中的代码中的check_connection()
  • Connection_errors_peer_address失败时,请注意服务器将错误ER_BAD_HOST_ERROR干净地返回给客户端,然后关闭套接字。这不同于突然关闭套接字(例如在崩溃中):前者应由客户端报告为"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中该客户端的行:它可能包含报告的 other 个失败。

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显示服务器强制关闭的某些连接。当会话闲置很长时间时,通常会发生这种情况。

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

  1. 客户端打开连接,并发送一些查询
  2. 然后客户端长时间不执行任何操作(大于net_read_timeout)
  3. 由于流量不足,服务器关闭了会话,并增加了Aborted_connects
  4. 然后,客户端发送另一个查询,发现连接已关闭,并报告“ MySQL已消失”

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

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

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