Многопоточное приложение MySQL и segfault при повторном подключении к MySQL в рабочий поток

У меня есть клиент многопоточного приложения для MySQL, и я использую C-клиент MySQL (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

и далее БТ:

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);
    }
}

person user1514692    schedule 01.04.2013    source источник
comment
Не могли бы вы показать нам трассировку, созданную в результате нарушения сегментации?   -  person alk    schedule 01.04.2013
comment
Да, трассировка указывала на функцию mysql_real_connect(). У меня было более 10 успешных переподключений и один sigfault :(. Итак, у меня был sigfault в mysql_ping().   -  person user1514692    schedule 01.04.2013
comment
Тогда почему бы не добавить трассировку стека к вашему сообщению. Это прояснило бы нам ситуацию.   -  person alk    schedule 01.04.2013
comment
Хорошо, трассировка стека добавлена ​​в пост   -  person user1514692    schedule 01.04.2013
comment
Поскольку я блокирую mysql_ping() с помощью мьютекса, у меня есть блокировка, поскольку у меня много запросов SQL. См. код 2.   -  person user1514692    schedule 01.04.2013
comment
Для меня значения для c в вызовах mysql_query_run выглядят подозрительно. Я бы не удивился, если бы это был мусор. В любом случае, я бы рекомендовал установить отладочные сборки mysql-libs, тогда следующие сбои предоставят вам трассировку стека, ссылающуюся на точную исходную строку сбоев, чтобы вы могли посмотреть, что там происходит.   -  person alk    schedule 01.04.2013


Ответы (1)


(Нашел это с помощью поиска в Google. Я знаю, что первоначальный автор вопроса, вероятно, давно ушел, но отвечаю для потомков на случай, если кто-то еще наткнется здесь.)

Ваша попытка повторного подключения невероятно сложна, и вы, вероятно, спотыкаетесь об это, чтобы вызвать эти сбои.

Во время попытки повторного подключения вы:

  1. Освободите свой мьютекс.
  2. Закройте соединение.
  3. Разрешить другим потокам пытаться использовать уже закрытое соединение, что приведет к ошибке сегментации. В конце концов, вы разблокировали мьютекс.
  4. Вызовите mysql_library_end() без какой-либо блокировки, что разрушит весь процесс libmysqlclient. Любая попытка запустить mysql_ping или большинство функций mysql_, отличных от mysql_connect, независимо от того, где и с какими параметрами, теперь будет segfault, и у вас по-прежнему нет блокировки, препятствующей другим потокам делать это.
  5. Вы восстанавливаете свою блокировку и вызываете mysql_library_init, а затем по какой-то причине снова освобождаете ее.
  6. Наконец, вы вызываете mysql_real_connect, сообщая ему, что нужно работать с общей переменной c->mysql без каких-либо блокировок. Это может выполняться несколькими потоками одновременно, и, следовательно, также может возникать ошибка сегментации.

Как видите, есть несколько моментов, в которых ваш код может быть аварийно завершен. Вот как это исправить:

  1. Удерживайте conn_mutex на протяжении всей операции, начиная с mysql_ping и до завершения попытки повторного подключения. Любой другой план приведет к тому, что ваши потоки будут бороться за то, кто попытается восстановить соединение.
  2. Не вызывайте mysql_library_end или mysql_library_init вообще - эти вызовы влияют на состояние всего процесса без уважительной причины и не имеют ничего общего с повторным установлением отдельных соединений. Вызывайте их только до того, как вы сделаете свое самое первое соединение в своей программе, и после того, как вы закроете самое последнее, и то только в одном потоке.
  3. Убедитесь, что ваш код вызывает mysql_thread_init для всех новых потоков и вызывает mysql_thread_end перед выходом из любого потока, кроме основного. (Эти вызовы также не зависят от какого-либо конкретного соединения, и их отсутствие может вызвать некоторые очень тонкие проблемы, потому что часто все по-прежнему в основном работает. проблема.)
  4. Убедитесь, что вы связываете свой код с потокобезопасным libmysqlclient_r, а не с обычным libmysqlclient, иначе ничто из вышеперечисленного не спасет вас.

Надеюсь, это поможет кому-то еще с похожими проблемами.

person Walter Mundt    schedule 25.02.2014