Python MySQLdb连接被多线程共享引发的内核segfault段错误
Python celery Worker exited prematurely: signal 11 (SIGSEGV) --一种解决方案
Python libmysqlclient segfault (MySQLdb)
Python多线程共享Mysql连接出错?
python mysqldb多线程问题
python自制简易mysql连接池的实现示例
MySQLdb、Mysqlclient、PyMySQL 三个python的MySQL库的比较和总结
问题
线上环境发现error

billiard.exceptions.WorkerLostError: Worker exited prematurely: signal 11 (SIGSEGV).
Process 'Worker-47' pid:25883 exited with 'signal 11 (SIGSEGV)'
resolve_monitor_alarms这个celery任务在worker执行时出现未知异常,并导致worker进程结束。
错误是signal 11 (SIGSEGV),这是一个linux signal信号,signal 11标识Segmentation violation,即段异常,涉及到linux内核的数据异常。
排查
这是一个系统内核的错误,那我们首先去看服务器的系统日志。
/var/log/messages
因为我们的任务是每小时的整点执行一次,缩小范围直接看每小时0分的日志。
经过简单的分析,发现每小时都会有这样一条错误日志


kernel: python[6406]: segfault at 1b8 ip 00007f24ea928fc8 sp 00007f24d3ffdc20 error 4 in libmysqlclient.so.18.0.0[7f24ea8f7000+2de000]
某个python进程在使用libmysqlclient库的使用遇到segfault段错误
那么问题就定位到mysql了,看下这个任务的具体代码
@classmethod
def alarm_resolved(cls, alarm_source_obj):
"""蓝监监控告警恢复"""
def _update_bk_monitor_alarms_status_thread(_alarm_list, _semaphore, _cursor):
# 线程量增加
_semaphore.acquire()
try:
# ...
sql = "xxx"
logger.debug(sql)
_cursor.execute(sql)
rows = _cursor.fetchall()
# ...
except Exception as error:
logger.exception("_update_bk_monitor_alarms_status_thread error, detail info: " "[error: %s]" % error)
finally:
# 线程量减少
_semaphore.release()
# ...
conn = MySQLdb.connect(host=host, user=user, password=password, database=database, charset="utf8")
cursor = conn.cursor()
# ...
# 分批去数据库获取告警事件状态, 分批的原因是mysql query查询大小有限制
once_query_num = 500
semaphore = threading.BoundedSemaphore(4)
threads = []
for alarm_list in [
active_alarms[i : i + once_query_num] for i in range(0, len(active_alarms), once_query_num) # noqa
]:
t = threading.Thread(target=_update_bk_monitor_alarms_status_thread, args=(alarm_list, semaphore, cursor))
threads.append(t)
t.start()
for t in threads:
t.join()
cursor.close()
conn.close()
使用的MySQLdb模块,打开一个数据库连接,然后把游标共享给多个线程去查询数据库。
这里其实是mysqlclient模块,MySQLdb的fork版本用来兼容python3
我试着给线程的开始和结束打了log,分析发现有一部分线程执行失败了没有结束log。
所以应该是某个子线程拿着游标去查询数据库时引发了内核segfault。
在StackOverflow上找到了如下解决办法:

为子线程开启自己的mysql连接
经过测试,确实解决了问题。
分析
很明显MySQLdb打开的连接不能被多线程共享,可能会引发内核问题。
看一下官方文档的解释:
threadsafety
Integer constant stating the level of thread safety the interface supports. This is set to 1, which means: Threads may share the module.
The MySQL protocol can not handle multiple threads using the same connection at once. Some earlier versions of MySQLdb utilized locking to achieve a threadsafety of 2. While this is not terribly hard to accomplish using the standard Cursor class (which uses mysql_store_result()), it is complicated by SSCursor (which uses mysql_use_result(); with the latter you must ensure all the rows have been read before another query can be executed. It is further complicated by the addition of transactions, since transactions start when a cursor execute a query, but end when COMMIT or ROLLBACK is executed by the Connection object. Two threads simply cannot share a connection while a transaction is in progress, in addition to not being able to share it during query execution. This excessively complicated the code to the point where it just isn't worth it.
The general upshot of this is: Don't share connections between threads. It's really not worth your effort or mine, and in the end, will probably hurt performance, since the MySQL server runs a separate thread for each connection. You can certainly do things like cache connections in a pool, and give those connections to one thread at a time. If you let two threads use a connection simultaneously, the MySQL client library will probably upchuck and die. You have been warned.
For threaded applications, try using a connection pool. This can be done using the Pool module.
有一个叫threadsafety的参数,控制线程安全的级别。
它被设置为1,意味着多线程只能共享模块。
- 0 多线程不能共享模块
- 1 多线程可以共享模块
- 2 多线程可以共享模块和连接
- 3 多线程可以共享模块、连接和游标
但是看了一下这只是一个声明,MySQLdb只支持threadsafety=1,也就是多线程只能共享模块,不能共享连接和游标。原因大概是2和3的实现较为昂贵和复杂。
MySQLdb不建议我们多线程共享连接,不值得花费精力去实现,并且可能带来性能问题,因为mysql server为每一个连接都会运行一个单独的线程。
MySQLdb建议我们使用连接池去缓存多个连接,缓存池初始化后,每个线程都从缓存池中去获取自己的连接,并在线程结束归还到连接池。
MySQLdb还指明,如果多个线程同时使用了一个mysql连接,我们的mysql client很可能会崩溃死掉。
解决办法
所以对我们的问题,有两个解决办法:
- 自己维护一个数据库连接池为每个线程分配单独的连接
- 直接在每个线程中打开新的连接
前者性能好,后者代码实现快。
总结
MySQLdb连接线程不安全导致的问题,需要为线程分配单独的数据库连接。
django.db是用连接池实现的,不会有问题。
但我们直接使用一些mysql模块的时候要注意线程安全问题。
Python MySQLdb连接被多线程共享引发的内核segfault段错误的更多相关文章
- python MySQLdb连接mysql失败(转载)
最近了解了一下django,数据库选用了mysql, 在连接数据库的过程中,遇到一点小问题,在这里记录一下,希望能够对遇到同样的问题的朋友有所帮助,少走一些弯路.关于django,想在这里也额外说一句 ...
- 7.Python网络编程_多线程共享全局变量问题
Python多线程支持全局变量的共享操作,但是它存在很多问题,先来看以下程序,该程序理论上执行完毕后全局变量g_num的值应该是2000000,但是在实际运行中,结果不足理论值 import thre ...
- python -- MySQLdb连接mysql数据库
1. python安装mysql $ pip install mysql-python 2. 数据库连接程序: import MySQLdb # 打开数据库连接db = MySQLdb.connect ...
- python MySQLdb连接mysql时报错
故障现象: >>> import MySQLdb >>> conn = MySQLdb.connect(host=,charset="utf8" ...
- 一起学Python: 多线程-共享全局变量问题
多线程-共享全局变量问题 多线程开发可能遇到的问题 假设两个线程t1和t2都要对全局变量g_num(默认是0)进行加1运算,t1和t2都各对g_num加10次,g_num的最终的结果应该为20. 但是 ...
- 一起学Python:多线程-共享全局变量
多线程-共享全局变量 from threading import Thread import time g_num = 100 def work1(): global g_num for i in r ...
- Python 中多线程共享全局变量的问题
写在前面不得不看的一些P话: Python 中多个线程之间是可以共享全局变量的数据的. 但是,多线程共享全局变量是会出问题的. 假设两个线程 t1 和 t2 都要对全局变量g_num (默认是0)进行 ...
- python多线程-共享全局变量
目录 多线程-共享全局变量 多线程-共享全局变量 列表当作实参传递到线程中 总结 多线程-共享全局变量问题 多线程开发可能遇到的问题 测试1 测试2 多线程-共享全局变量 多线程-共享全局变量 imp ...
- Python MySQLdb模块连接操作mysql数据库实例_python
mysql是一个优秀的开源数据库,它现在的应用非常的广泛,因此很有必要简单的介绍一下用python操作mysql数据库的方法.python操作数据库需要安装一个第三方的模块,在http://mysql ...
- python编程系列---多线程共享全局变量出现了安全问题的解决方法
多线程共享全局变量出现了安全问题的解决方法 当多线程共享全局变量时,可能出现安全问题,解决机制----互斥锁:即在在一段与全局变量修改相关的代码中,假设一个时间片不足以完成全局变量的修改,就在这段代码 ...
随机推荐
- llinux day02 基础操作 帮助 文件管理 马
免密码登录(只是为了方便教学,免了图形界面的密码) 1,Linux免密自动以root身份登录图形化界面,修改etc/gdm/custom.conf 在deamon下面添加两行,注意区分大小写 [dae ...
- Web开发 学习 调试 调优
目录 快捷操作 调试方法 基本调试方法 修改参数和请求重发 Chrome抓包分析 性能优化 安全 cURL请求 参考 参考:MDN 调试HTML 参考:什么是浏览器开发者工具? 参考:检查和编辑页面与 ...
- Flink Application Development DataStream API Operators Overview-- Flink应用程序开发DataStream API操作符概览
目录 概览 DataStream转换 物理分区 任务链和资源组 翻译原文- Application Development DataStream API Operators 概览 操作符将一个或多个D ...
- linux 查看端口号是否对外开放,并开放端口号
查看对外开放的端口状态查询指定端口是否已开 firewall-cmd --query-port=8780/tcp 提示 yes,表示开启:no表示未开启. 对外开发端口 查看想开的端口是否已开:fir ...
- c语言学习---gets()读取字符串,以及\0,fgets()put()fputs()
#include<stdio.h> //gets()读取字符串, 可以读取空格 int main() { char num[2] = "";//gets 也会造成内存污 ...
- ListView,ScrollView,RecyclerView上下滑动监听
在项目中有这样需求要对ListView或ScrollView或RecyclerView滚动进行监听,来做一些处理,下面来看对应实现 一:Listview上下滑动监听 通过实现AbsListView.O ...
- 剪裁正方形图片cropper
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8& ...
- mariadb(mysql) redis
mariadb(mysql) 安装 winodows 略 linux 用yum下载安装,先添加yum源,阿里的yum源mariadb版本比较老,要新版本的还是要用官方的源,具体的官方yum源最好去官网 ...
- Linux基础第六章:逻辑卷的使用、扩容和磁盘配额
一.逻辑卷的使用及扩容 1.概念优点及注意事项 2.使用命令及基本格式 3.创建逻辑卷 ①创建物理卷 ②创建卷组 ③创建逻辑卷 ④格式化.挂载yk26逻辑卷在/mnt下并在逻辑卷yk26下创建文件a. ...
- Redis集群(主从复制)
主从复制主从复制原理 Redis集群中有很多Redis服务器,这些Reids服务器分为主服务器和从服务器. 从服务器会向主服务器发送命令:SYNC命令. 主服务器接收到SYNC命令后,开始执行GBSA ...