В 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
Переменная состояния показывает некоторые соединения, принудительно закрытые сервером.Обычно это происходит, когда сеанс бездействует в течение очень долгого времени.
Типичный сценарий для этого:
- Клиент открывает соединение и отправляет некоторые запросы
- Затем клиент ничего не делает в течение продолжительного времени (больше, чем net_read_timeout)
- Из-за недостатка трафика сервер закрывает сеанс и увеличивает значение Aborted_connects
- Клиентзатем отправляет другой запрос, видит закрытое соединение и сообщает, что «MySQL ушёл»
Обратите внимание, что клиентское приложение, забывшее о чистом закрытии сеансов, выполнит 1-3, это может быть в случае Aborted_clientsна мастера.Некоторая очистка здесь для исправления клиентских приложений, использующих мастер, поможет уменьшить потребление ресурсов, так как оставление 151650 открытых сеансов для смерти по тайм-ауту требует затрат.
Клиентское приложение, выполняющее 1-4, может вызвать Aborted_clients на сервере и MySQL ушел на клиенте.Скорее всего, виновником является клиентское приложение, сообщающее «MySQL ушёл».
Если приложение мониторинга, скажем, проверяет сервер каждые N секунд, то убедитесь, что время ожидания (здесь 30 и 60 секунд)значительно больше, чем N, иначе сервер прекратит сеанс мониторинга.