【问题标题】:MySQL - 0 [ERROR] Error in accept: Bad file descriptorMySQL - 0 [错误] 接受错误:错误的文件描述符
【发布时间】:2016-10-14 06:45:42
【问题描述】:

最近在 Debian (Debian 3.2.78-1 x86_64 GNU/Linux) 上升级到 MySQL 5.7.12,并且每隔几个小时就会运行到服务器挂起。这被 syslog 和 mysql.log 淹没了:

2016-06-13T18:05:20.261209Z 0 [ERROR] Error in accept: Bad file descriptor

MySQL 信息: mysql Ver 14.14 Distrib 5.7.12-5,适用于使用 6.2 的 debian-linux-gnu (x86_64)

my.cnf mysqld 部分的片段可以指导调整值的一些帮助:

[mysqld]
max_allowed_packet      = 64M
thread_stack            = 256K
thread_cache_size       = 8

max_connections         = 150
max_connect_errors      = 10000
connect_timeout         = 30
wait_timeout            = 86400
table_open_cache        = 2048
open_files_limit        = 65535

query_cache_limit       = 4M
query_cache_size        = 128M
query_cache_type    = 1

server-id               = 1
log_bin                 = /var/log/mysql/mysql-bin.log
expire_logs_days        = 10
max_binlog_size         = 100M

# * InnoDB
innodb_file_per_table
innodb_buffer_pool_instances=2
innodb_buffer_pool_size=2G
thread_pool_size = 24

【问题讨论】:

    标签: mysql linux debian


    【解决方案1】:

    我们在带有 mysql 5.7.13 的 Ubuntu 16.04 系统上遇到了同样的问题。我们在 systemd 中增加了最大打开文件参数,如下所示:

    /etc/systemd/system/mysql.service.d/10-ulimit.conf

    [Service]
    LimitNOFILE=1000000
    

    到目前为止,该问题没有再次发生。也许mysql现在需要更多的文件描述符。

    【讨论】:

    • 提醒一下——在我的 FC24 系统上,当使用 DNF 更新 mariadb 时,systemd 文件被覆盖,这个问题再次出现。
    • 不幸的是,这个修复在这里不起作用。我在使用 MySQL 5.7.17 的新 Ubuntu 16.04 和 Ubuntu 16.10 实例上遇到了这个错误。 (顺便说一句,我认为有必要先创建 mysql.service 文件夹并运行systemctl daemon-reload)。
    【解决方案2】:

    我发现了问题(或者可能是其中一个问题)。以下是 mysqld 上 strace 的摘录:

    ...
    socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP) = 20
    write(2, "2017-01-29T22:22:45.433033Z 0 [N"..., 72) = 72
    setsockopt(20, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
    setsockopt(20, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
    bind(20, {sa_family=AF_INET6, sin6_port=htons(3306), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
    listen(20, 70)                          = 0
    fcntl(20, F_GETFL)                      = 0x2 (flags O_RDWR)
    fcntl(20, F_SETFL, O_RDWR|O_NONBLOCK)   = 0
     ...
    accept(20, {sa_family=AF_INET6, sin6_port=htons(58332), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 37
    rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTORER|SA_RESTART, 0x7f3ddeac84b0}, {SIG_DFL, [], 0}, 8) = 0
    getpeername(37, {sa_family=AF_INET6, sin6_port=htons(58332), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
    getsockname(37, {sa_family=AF_INET6, sin6_port=htons(3306), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
    open("/etc/hosts.allow", O_RDONLY)      = 38
    fstat(38, {st_mode=S_IFREG|0644, st_size=589, ...}) = 0
    read(38, "# /etc/hosts.allow: list of host"..., 4096) = 589
    read(38, "", 4096)                      = 0
    close(38)                               = 0
    open("/etc/hosts.deny", O_RDONLY)       = 38
    fstat(38, {st_mode=S_IFREG|0644, st_size=704, ...}) = 0
    read(38, "# /etc/hosts.deny: list of hosts"..., 4096) = 704
    close(38)                               = 0
    socket(PF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0) = 38
    connect(38, {sa_family=AF_LOCAL, sun_path="/dev/log"}, 110) = 0
    sendto(38, "<36>Jan 29 14:23:08 mysqld[13052"..., 72, MSG_NOSIGNAL, NULL, 0) = 72
    shutdown(20, SHUT_RDWR)                 = 0
    close(20)                               = 0
    
    poll([{fd=20, events=POLLIN}, {fd=22, events=POLLIN}], 2, -1) = 1 ([{fd=20, revents=POLLNVAL}])
    accept(-1, 0x7ffe6ebd7160, 0x7ffe6ebd70fc) = -1 EBADF (Bad file descriptor)
    write(2, "2017-01-29T22:23:08.109451Z 0 [E"..., 75) = 75
     ... rinse and repeat *REALLY* fast!
    

    在使用 tcp_wrappers 锁定我的系统时,我无意中将 mysqld 从 hosts.allow 和 hosts.deny 中取出。似乎在检查了 hosts.allow 和 hosts.deny 之后,mysqld 会按照您的预期关闭并关闭套接字。然而,他们立即开始轮询(现在不存在的)套接字的活动。

    我刚刚做了另一个测试,我的 tcp_wrappers 配置正确。当我从授权主机连接时,一切都很好;但是,当我从被阻止的地址连接时,会发生同样的问题。基于此,我建议使用其他工具来保护 mysqld 并使您的 tcp_wrappers 配置比防火墙更开放。话虽如此,该错误仍应修复!

    此修复尚未经受住时间的考验,因此,像往常一样,YMMV。希望对你有帮助

    尼克

    【讨论】:

    • 我遇到了完全相同的问题(日志被“错误的文件描述符”消息淹没并且 MySQL 无法正常工作)并且搜索了很多,直到我找到了这篇文章。在我的例子中,fail2ban 出于某种原因将 127.0.0.1 添加到 /etc/hosts.deny 以彻底破坏系统!一旦我删除它(并将 localhost 添加到 /etc/hosts.allow 以防止将来发生这种情况),事情就恢复了正常。故事的精神:作为预防措施,将 127.0.0.1 添加到 /etc/hosts.allow 以确保 fail2ban 不会妨碍您!
    • 我在 hosts.allow 和 hosts.deny 中添加了一些 mysqld 选项,然后当我发现 MySQL Workbench 无法正确连接时将它们删除。问题中提到的消息淹没了我的 error.log 文件,直到我用完磁盘空间。我停止了 mysql 服务,压缩了该日志文件,然后重新启动。现在一切似乎都很好。谢谢。
    【解决方案3】:

    研究了一下,发现如下;

    1. 也出现在 MariaDB 中

      https://lists.launchpad.net/maria-discuss/msg03060.html https://mariadb.atlassian.net/browse/MDEV-8995

    2. Percona 服务器/Percona XtraDB 集群

      https://groups.google.com/forum/#!topic/percona-discussion/Tu0S2OvYqKA

    3. 2010/2012 年的旧错误

      https://bugs.mysql.com/bug.php?id=48929 http://lists.mysql.com/commits/96472

    4. 一些有趣的信息(不应该发生)

      https://lists.mysql.com/mysql/97275

    [我为 Percona 工作]

    【讨论】:

      【解决方案4】:

      升级到 Percona Cluster 5.7.14-26.17-1.trusty 后我遇到了同样的问题。

      ulimit.conf 建议没有帮助,据我所知,我通过编辑 /etc/security/limits.conf 和 /etc/sysctl.conf 确保有足够的文件句柄.

      我可以通过远程登录到 3306 帖子然后断开连接来轻松重现这一点;然后服务器进入一个自旋记录这个错误。

      在我的环境中看起来很有希望的一个可怕的解决方法是避免在端口 3306 上使用 TCP 连接,而是使用 unix 套接字。

      您可以通过更改/etc/mysql/my.cnf中的端口号,然后使用socat从端口3306代理到套接字

      nohup socat TCP4-LISTEN:3306,fork UNIX-CONNECT:/var/run/mysqld/mysqld.sock&
      

      如果我在端口 3306 上 telnet 并断开连接,我不会引发问题。我打算报告一下随着时间的推移它的表现如何。

      FWIW,代码看起来好像有时会发生这种情况:

      for (uint retry= 0; retry < MAX_ACCEPT_RETRY; retry++)
      {
        socket_len_t length= sizeof(struct sockaddr_storage);
        connect_sock= mysql_socket_accept(key_socket_client_connection, listen_sock,
                                          (struct sockaddr *)(&cAddr), &length);
        if (mysql_socket_getfd(connect_sock) != INVALID_SOCKET ||
            (socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
          break;
      }
      if (mysql_socket_getfd(connect_sock) == INVALID_SOCKET)
      {
        /*
          accept(2) failed on the listening port, after many retries.
          There is not much details to report about the client,
          increment the server global status variable.
        */
        connection_errors_accept++;
        if ((m_error_count++ & 255) == 0) // This can happen often
          sql_print_error("Error in accept: %s", strerror(errno));
        if (socket_errno == SOCKET_ENFILE || socket_errno == SOCKET_EMFILE)
          sleep(1);             // Give other threads some time
        return NULL;
      }
      

      【讨论】:

      • socat 解决方法虽然丑陋,但效果很好,所以我向其他遇到此问题的人推荐它。看起来这是一个已经存在一段时间的问题,并且只能影响某些环境(请参阅下面的 Google 和@Roel)。
      • 更新。使用 socat 解决方法后,我仍然看到一些循环会产生这些错误,但它们似乎最终会终止,因此系统大致稳定。
      【解决方案5】:

      我来到这里时遇到了同样的错误,但没有一个解决方案有效,但是在我们进行了一些研究后,我们发现是 apparmor 拒绝了我们的日志目录导致错误的文件描述符错误消息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-20
        • 1970-01-01
        • 2017-03-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多