0

我有 MySQL 的多线程应用程序客户端,我使用 MySQL C 客户端 (libmysqlclient_r)。我有数据库连接池,我在创建线程工作者(pthread_create)之前打开连接。

每个工作人员在开始工作之前仅从连接池中获取单个连接,并在完成工作后将其放入池中。每个工人都使用它独特的连接。

但是,数据库服务器非常过载,并且 MySQL 客户端出现错误:MySQL“在查询期间丢失了与 MySQL 服务器的连接”或“MySQL 服务器已消失”。我的应用程序在工作线程中重新连接:

my_bool res = mysql_ping(c->mysql);
if (res) {
    mysql_close(c->mysql);
    mysql_thread_end();

    c->mysql = mysql_init(NULL);
    mysql_thread_init();                

    struct conn_desc *cd = &c->db->cds[c->num];
    syslog(LOG_ERR, "reconnect :[%s:%d]\t%s\tnew MySQL=%X tid=%X\n", cd->host,  cd->port, c->db->default_db_name, c->mysql, pthread_self());

    res = mysql_real_connect(c->mysql, cd->host, cd->login, cd->passwd, c->db->default_db_name, cd->port, NULL, 0);
    if (res == NULL) {
        syslog(LOG_ERR, "[restart ] reconnect Error\n");
        exit(1);
    }
}

有时,我对 mysql_ping() 或 mysql_real_connect() 有分段错误。为什么?我在工作线程之间使用单独的 mysql 连接。怎么了?正义如何?

 0  0x0000000000000000 in ?? ()
 1  0x00007ffff7a7fc29 in my_net_local_init () from /usr/lib64/mysql/libmysqlclient_r.so.16
 2  0x00007ffff7ab0144 in my_net_init () from /usr/lib64/mysql/libmysqlclient_r.so.16
 3  0x00007ffff7aab245 in **mysql_real_connect ()** from /usr/lib64/mysql/libmysqlclient_r.so.16
 4  0x000000000040e72c in mysql_query_run (c=0xc36760,
q=0x7fffca1fb670 "SELECT     `id`, `name` FROM `msg_dir` WHERE `owncrc` = 2831014197") at mysql.c:163
 5  0x000000000040fdf2 in mysql_load_user (uid=2831014197, online=0) at mysql.c:706
 6  0x0000000000406047 in get_mess_count (uid=2831014197, mid=0, online=0) at commands.c:158
 7  0x000000000040618c in cmd_get_all_mess_count (key=0x7fffa80bb074 "gamc|2831014197|0|0 ", data=0x0, data_len=0, ret=0x7fffca1fbbc0, ret_len=0x7fffca1fbbdc) at commands.c:194
 8  0x0000000000405f52 in execute_command (key=0x7fffa80bb074 "gamc|2831014197|0|0 ", data=0x0, data_len=0,
ret=0x7fffca1fbbc0, ret_len=0x7fffca1fbbdc) at commands.c:132
 9  0x000000000040c4be in memcache_get (loop=0x7fffa40008c0, mctx=0x7fffa80bb040) at mc.c:479
 10 0x000000000040d353 in memcached_client (loop=0x7fffa40008c0, io=0x7fffa80bb040, revents=1) at mc.c:785
 11 0x00007ffff61e5071 in ev_invoke_pending () from /usr/lib64/libev.so.4
 12 0x00007ffff61ea23a in ev_run () from /usr/lib64/libev.so.4
 13 0x000000000040b5ec in ev_loop (loop=0x7fffa40008c0, flags=0) at /usr/include/libev/ev.h:810
 14 0x000000000040e24c in worker_listen (arg=0x10) at mc.c:1126
 15 0x00007ffff762c851 in start_thread () from /lib64/libpthread.so.0
 16 0x00007ffff5d2f6dd in clone () from /lib64/libc.so.6

和下一个BT:

0  0x00000000009f3f70 in ?? ()
1  0x00007ffff7aaf32a in net_real_write () from /usr/lib64/mysql/libmysqlclient_r.so.16
2  0x00007ffff7aaf63b in net_flush () from /usr/lib64/mysql/libmysqlclient_r.so.16
3  0x00007ffff7aaf901 in net_write_command () from /usr/lib64/mysql/libmysqlclient_r.so.16
4  0x00007ffff7aac6a9 in cli_advanced_command () from /usr/lib64/mysql/libmysqlclient_r.so.16
5  0x00007ffff7a7b1fd in **mysql_ping** () from /usr/lib64/mysql/libmysqlclient_r.so.16
6  0x000000000040e8f1 in mysql_query_run (c=0x9ed930,
q=0x7fff6fffe670 "SELECT `invisible` FROM meetre.autho2 WHERE `crc` = 1032552218") at mysql.c:164
7  0x00000000004107a0 in mysql_load_user (uid=1032552218, online=1) at mysql.c:858
8  0x0000000000406278 in get_mess_count (uid=1032552218, mid=0, online=1) at commands.c:165
9  0x00000000004063bd in cmd_get_all_mess_count (key=0x7fff90383f84 "gamc|1032552218|0|1 ", data=0x0, data_len=0,
ret=0x7fff6fffebc0, ret_len=0x7fff6fffebdc) at commands.c:201
10 0x0000000000406182 in execute_command (key=0x7fff90383f84 "gamc|1032552218|0|1 ", data=0x0, data_len=0,
ret=0x7fff6fffebc0, ret_len=0x7fff6fffebdc) at commands.c:135
11 0x000000000040c718 in memcache_get (loop=0x7fff5c0008c0, mctx=0x7fff90383f50) at mc.c:459
12 0x000000000040d5cb in memcached_client (loop=0x7fff5c0008c0, io=0x7fff90383f50, revents=1) at mc.c:765
13 0x00007ffff61e5071 in ev_invoke_pending () from /usr/lib64/libev.so.4
14 0x00007ffff61ea23a in ev_run () from /usr/lib64/libev.so.4
15 0x000000000040b81c in ev_loop (loop=0x7fff5c0008c0, flags=0) at /usr/include/libev/ev.h:810
16 0x000000000040e4f4 in worker_listen (arg=0x1e) at mc.c:1106
17 0x00007ffff762c851 in start_thread () from /lib64/libpthread.so.0
18 0x00007ffff5d2f6dd in clone () from /lib64/libc.so.6

代码2:

pthread_mutex_lock(&conn_mutex);
my_bool res = mysql_ping(c->mysql);
pthread_mutex_unlock(&conn_mutex);

if (res != OK) {
    mysql_close(c->mysql);              
    mysql_library_end();

    pthread_mutex_lock(&conn_mutex);
    mysql_library_init(0, NULL, NULL);
    pthread_mutex_unlock(&conn_mutex);

    c->mysql = mysql_init(NULL);

    struct conn_desc *cd = &c->db->cds[c->num];
    syslog(LOG_ERR, "reconnect :[%s:%d]\t%s\tnew MySQL=%X tid=%X %s\n", cd->host,  cd->port, c->db->default_db_name, c->mysql, pthread_self(), mysql_error(c->mysql));
    res = mysql_real_connect(c->mysql, cd->host, cd->login, cd->passwd, c->db->default_db_name, cd->port, NULL, 0);

    if (res == NULL) {
        syslog(LOG_ERR, "[restart ] reconnect Error\n");
        exit(1);
    }
}
4

1 回答 1

2

(通过谷歌搜索找到这个。我知道这个问题的原始作者可能早已不复存在,但为了防止其他人在这里绊倒,请为后代做出回应。)

您的重新连接尝试非常复杂,您可能会绊倒导致这些崩溃。

在重新连接尝试期间,您:

  1. 释放你的互斥锁。
  2. 关闭连接。
  3. 允许其他线程尝试使用现在关闭的连接,这将 segfault。毕竟,您解锁了互斥锁。
  4. 在没有任何锁定的情况下调用 mysql_library_end(),这会在整个进程范围内拆除 libmysqlclient。任何运行 mysql_ping 或除 mysql_connect 之外的大多数 mysql_ 函数的尝试,无论在何处或使用哪些参数,现在都会出现 segfault,并且您仍然没有阻止其他线程执行此操作的锁。
  5. 您重新获得锁并调用 mysql_library_init,然后由于某种原因再次释放它。
  6. 最后调用 mysql_real_connect,告诉它对共享的 c->mysql 变量进行操作,而不需要任何锁定。这可以由多个线程同时运行,因此也可能出现段错误。

如您所见,您的代码有几个地方可能会崩溃。以下是解决方法:

  1. 从 mysql_ping 之前到重新连接尝试完成之后,在整个操作中保留您的 conn_mutex。任何其他计划都将导致您的线程争夺谁可以尝试重新连接。
  2. 根本不要调用 mysql_library_end 或 mysql_library_init ——这些调用无缘无故地影响进程范围的状态,与重新建立单个连接无关。仅在您在程序中建立第一个连接之前以及在您关闭最后一个连接之后调用它们,然后仅在一个线程上调用。
  3. 确保您的代码在所有新线程上调用 mysql_thread_init,并在除主线程之外的任何线程退出之前调用 mysql_thread_end。(这些调用也独立于任何特定的连接,并且将它们排除在外可能会导致一些非常微妙的问题,因为事情通常仍然大部分工作。例如,如果 LOAD DATA INFILE 段错误在您身上,这可能是您的问题。)
  4. 确保您将代码与线程安全的 libmysqlclient_r 而不是常规的 libmysqlclient 链接,否则以上都不会拯救您。

希望能帮助其他有类似问题的人。

于 2014-02-25T18:05:09.713 回答