Многопоточное приложение MySQL и segfault при переподключении к MySQL в рабочий поток
У меня есть многопоточное приложение-клиент для MySQL, и я использую MySQL C-клиент (libmysqlclient_r). У меня есть пул соединений базы данных, где я открываю соединение перед созданием рабочих потоков (pthread_create).
Каждый работник получает только одно соединение из пула соединений перед началом работы и помещает его в пул после завершения работы. Каждый работник использует свое уникальное соединение.
Но сервер базы данных очень перегружен, и у клиента MySQL есть ошибки: MySQL "Потеряно соединение с сервером MySQL во время запроса" или "Сервер MySQL пропал". Мое приложение makeconnect в рабочем потоке:
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
и nex 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);
}
}
1 ответ
(Обнаружил это через поиск в Google. Я знаю, что первоначальный автор вопроса, вероятно, давно ушел, но отвечает за потомство, если кто-то еще споткнется здесь.)
Ваша попытка переподключения невероятно усложнена, и вы, вероятно, перепутали это, чтобы вызвать эти сбои.
Во время попытки повторного подключения вы:
- Отпусти свой мьютекс.
- Закройте соединение.
- Разрешить другим потокам использовать закрытое соединение, что приведет к сбою. В конце концов, вы разблокировали мьютекс.
- Вызовите mysql_library_end() без каких-либо блокировок, что разрушает libmysqlclient по всему процессу. Любая попытка запустить mysql_ping или большинство функций mysql_, отличных от mysql_connect, независимо от того, где или с какими параметрами, теперь будет вызывать ошибку, и у вас все еще нет блокировки, препятствующей тому, чтобы другие потоки делали это.
- Вы повторно запрашиваете свою блокировку и вызываете mysql_library_init, а затем снова отпускаете ее по какой-то причине.
- Наконец, вы вызываете mysql_real_connect, чтобы заставить его работать с общей переменной c->mysql без какой-либо блокировки. Это может быть запущено несколькими потоками одновременно, и, следовательно, может также вызывать segfault.
Как вы можете видеть, есть несколько моментов, когда ваш код подвержен сбоям. Вот как это исправить:
- Удерживайте ваш conn_mutex для всей операции, начиная с mysql_ping и заканчивая попыткой повторного подключения. Любой другой план приведет к тому, что ваши темы будут сражаться за тех, кто попытается восстановить соединение.
- Ни в коем случае не вызывайте mysql_library_end или mysql_library_init - эти вызовы влияют на состояние всего процесса без какой-либо веской причины и не имеют ничего общего с восстановлением отдельных соединений. Вызывайте их только до того, как вы установили свое первое соединение в своей программе, и после того, как закрыли свое последнее, и только в одном потоке.
- Убедитесь, что ваш код вызывает mysql_thread_init во всех новых потоках и вызывает mysql_thread_end перед выходом любых потоков, кроме вашего основного. (Эти вызовы также не зависят от какого-либо конкретного соединения, и их исключение может вызвать некоторые очень тонкие проблемы, потому что часто все еще в основном работает. Если, например, LOAD DATA INFILE работает с вами, это, вероятно, ваша проблема.)
- Убедитесь, что вы связываете свой код с потокобезопасным libmysqlclient_r, а не с обычным libmysqlclient, или ничего из вышеперечисленного не спасет вас.
Надеюсь, что это поможет кому-то еще с похожими проблемами.