Python 多线程查询 mysql 报错,求解决办法

2018-04-25 19:36:11 +08:00
 wsds
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-04-25 18:04:06
# @Author  : Your Name (you@example.org)
# @Link    : http://example.org
# @Version : $Id$

import threading,time,MySQLdb

class TestMysql():

	def __init__(self, ip, port, username, password, database):
		self.db = MySQLdb.connect(host=ip, port=port, user=username, passwd=password, db=database, charset="utf8")
		self.cursor = self.db.cursor()

	def select1(self,tablename):
		sql = "SELECT (gender & 15) AS gender1, f.image_data,f.json FROM intellif_face." + tablename + " f limit 100000;"
		self.cursor.execute(sql)
		data = self.cursor.fetchone()

		print(data)

	def select2(self,tablename):
		sql = "SELECT (race & 15) AS race1,f.image_data,f.json FROM intellif_face." + tablename + " f limit 100000;"
		self.cursor.execute(sql)
		data = self.cursor.fetchone()

		print(data)




if __name__ == '__main__':
	test = TestMysql("192.168.11.128", 3306, "root", 'introcks1234', "intellif_face")
	tablelist = ['t_face_24','t_face_25','t_face_26']  # 可以增加表名
	thread = []
	for tablename in tablelist:
		print(tablename)
		t1 = threading.Thread(target = test.select1, args = (tablename,))
		t2 = threading.Thread(target = test.select2, args = (tablename,))
		thread.append(t1)
		thread.append(t2)
	for t in thread:
                t.setDaemon(True)
		t.start()
	t.join()


============================output=========================================

Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Python36\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
  File "C:\Python36\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "F:\code\pyProject\thread\thread_mysql_test.py", line 25, in select2
    self.cursor.execute(sql)
  File "C:\Python36\lib\site-packages\MySQLdb\cursors.py", line 250, in execute
    self.errorhandler(self, exc, value)
  File "C:\Python36\lib\site-packages\MySQLdb\connections.py", line 50, in defaulterrorhandler
    raise errorvalue
  File "C:\Python36\lib\site-packages\MySQLdb\cursors.py", line 247, in execute
    res = self._query(query)
  File "C:\Python36\lib\site-packages\MySQLdb\cursors.py", line 411, in _query
    rowcount = self._do_query(q)
  File "C:\Python36\lib\site-packages\MySQLdb\cursors.py", line 374, in _do_query
    db.query(q)
  File "C:\Python36\lib\site-packages\MySQLdb\connections.py", line 277, in query
    _mysql.connection.query(self, query)
_mysql_exceptions.OperationalError: (2013, 'Lost connection to MySQL server during query')

.......

[Finished in 1.9s with exit code 3221226356]

3598 次点击
所在节点    Python
18 条回复
ericls
2018-04-25 20:32:25 +08:00
执行前检查是否需要重新连接 如果需要就重新连接
tkmiles
2018-04-25 20:37:04 +08:00
我貌似记得 mysql 会杀死空闲连接的
wsds
2018-04-25 20:39:28 +08:00
@ericls 怎么检查
testsec
2018-04-25 21:45:36 +08:00
用连接池
Aliencn
2018-04-25 21:50:44 +08:00
数据库承受不住这么大的查询吧,把 sql 语句的 limit 改成 1 试试。
lesteryu
2018-04-25 21:57:34 +08:00
MySQLdb 的 connections 和 cursors 都不 thread safe,不同的 threads 不能用同一个 connection 或者 cursor。
MerleLiuKun
2018-04-26 08:49:13 +08:00
check connection 可以用 ping(). 感觉可以用 MySQLdb 提供的 pool.
wsds
2018-04-26 09:01:33 +08:00
@Aliencn 一样的不行
wsds
2018-04-26 09:04:17 +08:00
@lesteryu 5 个方法各连一次 MySQl ?
xpresslink
2018-04-26 09:16:11 +08:00
要每个线程单独用一个 连接,把 test = TestMysql("192.168.11.128", 3306, "root", 'introcks1234', "intellif_face")
放在 for 里面就可以了

if __name__ == '__main__':

□□□□tablelist = ['t_face_24','t_face_25','t_face_26'] # 可以增加表名
□□□□thread = []
□□□□for tablename in tablelist:
□□□□□□□□test = TestMysql("192.168.11.128", 3306, "root", 'introcks1234', "intellif_face")
□□□□□□□□print(tablename)
□□□□□□□□t1 = threading.Thread(target = test.select1, args = (tablename,))
□□□□□□□□t2 = threading.Thread(target = test.select2, args = (tablename,))
□□□□□□□□thread.append(t1)
□□□□□□□□thread.append(t2)
□□□□for t in thread:
t.setDaemon(True)
t.start()
□□□□t.join()
Hopetree
2018-04-26 09:24:59 +08:00
借楼问一下,往 MySQL 里面插入多个数据的做法,有两种做法①开启一个连接,然后循环插入数据,最后断开连接②循环(开启连接,插入,断开)这个步骤,也就是每次插入都重新练级,我一直有点疑惑这两种方式,从性能和安全等给方面考虑,应该是采取哪一种?我用 Python 的时候喜欢用第一种,里面使用 try 语句来插入
wsds
2018-04-26 09:32:09 +08:00
@xpresslink 那就是要每个方法中各设各的连接咯,偷懒放到 init 里边,看来还是得独立出来
xpresslink
2018-04-26 09:52:34 +08:00
@wsds
这个只是临时方案,连接太多数据库也受不了,
而且每个线程创建了连接要关闭。不然时间长了爆内存了。

实际上你应该弄个连接池,每个线程到连接池取一个打开的连接,超过连接数就等待。

连接最后在退出时候统一关闭。
wsds
2018-04-26 10:08:54 +08:00
@xpresslink 连接池怎么搞?
long88
2018-04-26 15:56:18 +08:00
@wsds 可以用 DBUtils
wsds
2018-04-26 16:17:24 +08:00
@long88 我查一下这个
Minys
2018-04-27 00:11:10 +08:00
@xpresslink 自己写的线程池操作也可能有意想不到的危险,因为 Python 的 GIL,多线程的时候其实是通过 CPU 不断切换执行,让用户看起来像多线程。这就会出现 thread1 和 thread2 在对同一个对象操作的时候出现问题。

比如两个 thread1 和 thread2 都要执行对 A.a+=1 的操作,但是这个看似基本的操作其实分 3 步:
1. tmp=getattr(A, 'a')
2. tmp =tmp+1
3. setattr(A, 'a', tmp)

如果刚好在这中间切换线程,就会导致奇怪的 bug,根本无法 debug,感觉还是使用 multiprocessing 里面的 Pool 来直接多进程操作比较好。多线程的话还是得用 threading 库确保不会冲突,必要的时候还是得上锁。
sujin190
2018-04-27 20:27:02 +08:00
首先 MySQLdb 并不是线程安全的,而且 mysql 协议是 request respond 模式的,并不能在上一个查询未完成之前发送下一个查询请求,这种基础问题随便一查都知道的吧,多线程不加锁挂才是正常的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/449848

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX