2

我的 MariaDB 服务器在 600 秒(10 分钟)不活动后使我的 C++ 客户端(使用 libmariadb)超时,我不知道为什么,因为我找不到任何指定该数字的配置超时。

这是我的代码,我在其中执行一个简单的 SELECT 查询,等待 11 分钟,然后再次运行相同的查询并得到“服务器消失”错误:

#include <iostream>
#include <unistd.h>

#include <errmsg.h>
#include <mysql.h>

int main(int, char**)
{
    // connect to the database
    MYSQL* connection = mysql_init(NULL);
    my_bool reconnect = 0;
    mysql_options(connection, MYSQL_OPT_RECONNECT, &reconnect);  // don't implicitly reconnect
    mysql_real_connect(connection, "127.0.0.1", "testuser", "password",
                       "my_test_db", 3306, NULL, 0);

    // run a simple query
    mysql_query(connection, "select 5");
    mysql_free_result(mysql_store_result(connection));
    std::cout << "First query done...\n";

    // sleep for 11 minutes
    sleep(660);

    // run the query again
    if(! mysql_query(connection, "select 5"))
    {
        std::cout << "Second query succeeded after " << seconds << " seconds\n";
        mysql_free_result(mysql_store_result(connection));
    }
    else
    {
        if(mysql_errno(connection) == CR_SERVER_GONE_ERROR)
        {
            // **** this happens every time ****
            std::cout << "Server went away after " << seconds << " seconds\n";
        }
    }

    // close the connection
    mysql_close(connection);
    connection = nullptr;

    return 0;
}

服务器进程的标准输出报告它使我的连接超时:

$ sudo journalctl -u mariadb
...
Jul 24 17:58:31 myhost mysqld[407]: 2018-07-24 17:58:31 139667452651264 [Warning] Aborted connection 222 to db: 'my_test_db' user: 'testuser' host: 'localhost' (Got timeout reading communication packets)
...

查看 tcpdump 捕获,我还可以看到服务器向客户端发送 TCP FIN 数据包,这会关闭连接。

我被难住的原因是因为我没有更改任何默认超时值,甚至都不是 600 秒:

MariaDB [(none)]> show variables like '%timeout%';
+-------------------------------------+----------+
| Variable_name                       | Value    |
+-------------------------------------+----------+
| connect_timeout                     | 10       |
| deadlock_timeout_long               | 50000000 |
| deadlock_timeout_short              | 10000    |
| delayed_insert_timeout              | 300      |
| innodb_flush_log_at_timeout         | 1        |
| innodb_lock_wait_timeout            | 50       |
| innodb_print_lock_wait_timeout_info | OFF      |
| innodb_rollback_on_timeout          | OFF      |
| interactive_timeout                 | 28800    |
| lock_wait_timeout                   | 31536000 |
| net_read_timeout                    | 30       |
| net_write_timeout                   | 60       |
| slave_net_timeout                   | 3600     |
| thread_pool_idle_timeout            | 60       |
| wait_timeout                        | 28800    |
+-------------------------------------+----------+

那么为什么服务器会超时我的连接?根据文档,我原以为是因为wait_timeoutserver 变量,但默认为 8 小时...

顺便说一句,我正在使用 MariaDB 10.0 和 libmariadb 2.0(来自 Ubuntu Xenial Universe 存储库)


编辑:这是捕获断开连接的 tcpdump 捕获的图像。我的 Wireshark 过滤器是tcp.port == 55916,所以我正在查看进出这个客户端连接的流量。服务器发送的 FIN 数据包是数据包 1199,正好在前一个数据包 (884) 之后 600 秒。 在wireshark中打开pcap

4

3 回答 3

6

wait_timeout很棘手。从同一个连接做

SHOW SESSION VARIABLES LIKE '%timeout%';
SHOW SESSION VARIABLES WHERE VALUE BETWEEN 500 AND 700;

您应该能够通过执行来解决该问题

mysql_query("SET @@wait_timeout = 22222");

您是否以“root”身份连接?

更多连接器详细信息:

见: https ://dev.mysql.com/doc/refman/5.5/en/mysql-options.html

CLIENT_INTERACTIVE:在关闭连接之前允许interactive_timeout 秒不活动(而不是wait_timeout 秒)。客户端的会话 wait_timeout 变量设置为会话 interactive_timeout 变量的值。

https://dev.mysql.com/doc/relnotes/connector-cpp/en/news-1-1-5.html(MySQL连接器/C++ 1.1.5)

也可以使用 MySQL_Statement::getQueryTimeout() 和 MySQL_Statement::setQueryTimeout() 方法获取和设置语句执行时间限制。

也可能存在 TCP/IP 超时。

于 2018-07-24T22:10:19.440 回答
1

我不确定确切的原因。但我敢肯定wait_timeout,这不是唯一对此有影响的东西。根据您在问题中包含的唯一错误消息,读取数据包似乎有问题。

Got timeout reading communication packets

我相信这更像是 MariaDB 在读取数据包而不是尝试连接时遇到问题。我还查看了 MariaDB 客户端库,发现了这个块;

if (ma_net_write_command(net,(uchar) command,arg,
            length ? length : (ulong) strlen(arg), 0))
  {
    if (net->last_errno == ER_NET_PACKET_TOO_LARGE)
    {
      my_set_error(mysql, CR_NET_PACKET_TOO_LARGE, SQLSTATE_UNKNOWN, 0);
      goto end;
    }
    end_server(mysql);
    if (mariadb_reconnect(mysql))
      goto end;
    if (ma_net_write_command(net,(uchar) command,arg,
              length ? length : (ulong) strlen(arg), 0))
    {
      my_set_error(mysql, CR_SERVER_GONE_ERROR, SQLSTATE_UNKNOWN, 0);
      goto end;
    }
}

https://github.com/MariaDB/mariadb-connector-c/blob/master/libmariadb/mariadb_lib.c

因此,当它遇到数据包大小问题时,它似乎将错误代码设置为服务器消失。建议你把max_allowed_packet变量改成大一点的值,看看有没有效果。

SET @@global.max_allowed_packet = <some large value>;

https://mariadb.com/kb/en/library/server-system-variables/#max_allowed_pa​​cket

我希望它会有所帮助,或者至少它会让您找到解决问题的途径:) 最后,我认为您应该处理代码中的断开连接而不是依赖超时。

于 2018-07-25T05:27:35.710 回答
0

具有 Haproxy 负载平衡的 Galera 集群。在 haproxy 设置上更改此参数

defaults
    timeout connect 10s
    timeout client 30s
    timeout server 30s
于 2019-05-30T05:15:13.467 回答