MySQL ушел: Connection_errors_peer_address с большими числами - PullRequest
0 голосов
/ 25 сентября 2018

У нас есть репликация master-slave 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 голосов
/ 01 октября 2018

В 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 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, по сути, делает Обратный поиск DNS , чтобы:

  • найти имя хоста клиента
  • найдите IP-адрес, соответствующий этому имени хоста, чтобы позже снова преобразовать этот IP-адрес в имя хоста (см. Следующий вызов ip_to_hostname ()).

В целом, сбои учтеныConnection_errors_peer_address может быть связано с загрузкой системы (вызывающей временные сбои, такие как нехватка памяти и т. Д.) Или с сетевыми проблемами, влияющими на DNS.

Раскрытие информации: я оказался человеком, который внедрил эту переменную состояния Connection_errors_peer_addressв MySQL, как часть усилий по улучшению видимости / наблюдаемости в этой области кода.

[Редактировать] Чтобы получить более подробную информацию и / или рекомендации:

  • Wкурица 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 исчезнут.
  • 323 из 55 миллионов соединений действительно кажутся временными.Предполагая, что клиент мониторинга когда-нибудь подключится правильно, проверьте строку в таблице 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] На основании новых доступных данных:

The 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, иначе сервер прекратит сеанс мониторинга.

...