文章目录

  • 一、例子
  • 二、报错及原因
  • 三、解决方法
    • 1.在每个execute前加上互斥锁
    • 2.在pool1.map(func, list)中参数的func函数中,实例化一个数据库对象
    • 3.在KsMySql数据库链接类中使用数据库链接池获取链接,将pool链接池为类对象

一、例子

  • 需求
    使用多线程下载视频到本地,将视频的名字保存在数据库表中,数据库表中不能保存重复视频名字
  • demo.py
from multiprocessing.dummy import Pool
import traceback
import requests
import pymysql
import os
# 习惯函数名开头大写,变量名开头小写,还没适应Python写代码规范,见谅# 数据库链接类
class KsMySql:def __init__(self):self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='1234567', db='pythonspider', charset='utf8mb4') # 普通链接self.cursor = self.conn.cursor()# 是否保存过了通过视频名称查找, 返回true为不存在,false为存在def IsSaveVideoByName(self, filename):try:self.cursor.execute('select * from ksvideoinfo where filename = "%s"' %(filename))result = self.cursor.fetchone()return result is not Noneexcept:print('IsSaveVideoByName 查询错误')traceback.print_exc()return True# 插入视频信息def SaveVideoInfo(self, filename):try:self.cursor.execute('insert into ksvideoinfo(filename) values("%s")'%(filename))self.conn.commit()print('SaveVideoInfo 插入数据成功')except Exception as e:self.conn.rollback()print('SaveVideoInfo 插入数据错误')print(e)traceback.print_exc()def __del__(self):self.cursor.close()self.conn.close()# 全局变量
ksmysql = KsMySql()# 数据库类实例
infolist = []
dirName = 'E:/AllWorkSpace1/Pytharm/pythonProjectPaWeb/Testdemo'# 保存目录
if not os.path.exists(dirName):os.mkdir(dirName)def Select():name = '杨洋迪丽热巴《烟火星辰》,用歌声致敬中国航天'# 需求3:数据库表中不能保存重复视频名字(这里只是模拟)isSave = ksmysql.IsSaveVideoByName(name)# 为了方便,默认为不存在,直接添加url到list中mp4url = 'https://video.pearvideo.com/mp4/short/20220206/cont-1751191-15823342-hd.mp4'infolist.append({'name': name, 'videoUrl': mp4url})def SaveInfo(dic):name = dic['name']pathName = dirName + '/' + name + '.mp4'url = dic['videoUrl']try:# if not os.path.exists(pathName):mp4Data = requests.get(url=url).content # 从网络下载视频with open(pathName, 'wb') as f:# 需求1:视频保存在本地f.write(mp4Data)print(name, "下载完成")# else:#     print(name,'已存在,无需下载')# 需求2:视频的名字保存在数据库表中ksmysql.SaveVideoInfo(name)except Exception as e:print(name, '下载失败失败或者保存数据库失败')print(e)traceback.print_exc()def Main():pool1 = Pool(20) # 线程池for cur in range(0, 100):infolist.clear()Select()pool1.map(SaveInfo, infolist) # 使用多线程下载pool1.close()pool1.join()Main()

二、报错及原因

  • 常见错误
    1). Packet sequence number wrong
    2). Exception _mysql_exceptions.OperationalError: (2013, ‘Lost connection to MySQL server during query’)
    3). pymysql AttributeError: ‘NoneType‘ object has no attribute ‘settimeout‘
  • 原因
    如上demo.py,是因为各个线程共享同一个数据库链接而导致的错误

三、解决方法

1.在每个execute前加上互斥锁

如:

...同上
import threading
class KsMySql:def __init__(self):self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='tiger', db='pythonspider', charset='utf8mb4') # 普通链接self.cursor = self.conn.cursor()self.lock = threading.Lock()# 实例化# 是否保存过了通过视频名称查找, 返回true为不存在,false为存在def IsSaveVideoByName(self, filename):try:self.lock.acquire() # 上锁self.cursor.execute('select * from ksvideoinfo where filename = "%s"' %(filename))result = self.cursor.fetchone()self.lock.release() # 解锁return result is not Noneexcept:print('IsSaveVideoByName 查询错误')traceback.print_exc()return True
...同上

但经过我个人测试发现,没有用,还是会报新错,这个方法理论上是没问题的,但是在multiprocessing.dummy多线程情况下却不行。仅代表我个人想法,也许自己能力不足,哪里写错了

2.在pool1.map(func, list)中参数的func函数中,实例化一个数据库对象

...
def SaveInfo(dic):ksmysql = KsMySql()# 数据库类实例name = dic['name']pathName = dirName + '/' + name + '.mp4'url = dic['videoUrl']try:# if not os.path.exists(pathName):mp4Data = requests.get(url=url).content # 从网络下载视频with open(pathName, 'wb') as f:# 需求1:视频保存在本地f.write(mp4Data)print(name, "下载完成")# else:#     print(name,'已存在,无需下载')# 需求2:视频的名字保存在数据库表中ksmysql.SaveVideoInfo(name)except Exception as e:print(name, '下载失败失败或者保存数据库失败')print(e)traceback.print_exc()
...

可以完美解决,因为这样每个线程都有自己的数据库链接对象。
优点:简单、方便
缺点:每调用SaveInfo函数一次就建立一个数据库链接,并函数结束时关闭链接,可能性能有损

3.在KsMySql数据库链接类中使用数据库链接池获取链接,将pool链接池为类对象

...
from dbutils.pooled_db import PooledDBclass KsMySql:pool = Nonedef __init__(self):# self.conn = pymysql.Connect(host='127.0.0.1', port=3306, user='root', password='tiger', db='pythonspider', charset='utf8mb4') # 普通链接,每实例化一个对象就会新建一个链接self.conn = KsMySql.Getmysqlconn()# 从链接池中获取链接self.cursor = self.conn.cursor()# 静态方法@staticmethoddef Getmysqlconn():if KsMySql.pool is None:mysqlInfo = {"host": '127.0.0.1',"user": 'root',"passwd": 'tiger',"db": 'pythonspider',"port": 3306,"charset": 'utf8mb4'}KsMySql.pool = PooledDB(creator=pymysql, mincached=1, maxcached=20, host=mysqlInfo['host'],user=mysqlInfo['user'], passwd=mysqlInfo['passwd'], db=mysqlInfo['db'],port=mysqlInfo['port'], charset=mysqlInfo['charset'], blocking=True)print(KsMySql.pool)# else:# print('新KsMySql实例,从数据库链接池获取链接')return KsMySql.pool.connection()...def __del__(self):# 链接不是真正的被关闭,而是放回链接池中self.cursor.close()self.conn.close()def SaveInfo(dic):ksmysql = KsMySql()# 同样要写上实例化数据库类对象...
...

注意

KsMySql.pool = PooledDB(creator=pymysql, mincached=1, maxcached=20, host=mysqlInfo['host'],user=mysqlInfo['user'], passwd=mysqlInfo['passwd'], db=mysqlInfo['db'],port=mysqlInfo['port'], charset=mysqlInfo['charset'], blocking=True)
'''
blocking参数,代表当链接都被占用了,是否等待新的空闲链接
True :等待, 可能影响程序速度
False:不等待,(个人猜测。。好像是代表同用已占有的数据库链接对象,会重复一开始的报错),反正会报错,最好写成True
'''

可以完美解决,因为这样每个线程也都有自己的数据库链接对象。
优点:从链接池中获取自己的链接,优化点性能把
缺点:代码稍微复杂,坑多。。。

python pymysql multiprocessing.dummy多线程 读写数据库报错相关推荐

  1. python pymysql 多线程 读写数据库 报错 Packet sequence number wrong

    多线程连接数据,提交增改查请求,爆出Packet sequence number wrong - got 7 expected 2(数据包序列号错误) 原因:这是因为多线程共享了同一个数据库连接,但每 ...

  2. python 多进程multiprocessing进程池pool tensorflow-yolov3 报错 MemoryError

    进程数设置为1-9个都能正常运行,设置成10个就开始报错,怪事! D:\20191031_tensorflow_yolov3\python\python.exe D:/20191031_tensorf ...

  3. python 多进程multiprocessing进程池pool tensorflow-yolov3 报错TypeError: 'ApplyResult' object is not iterable

    首先,代码结构它长这样: 可每次调用线程池进行识别时,就会报如下错误: D:\20191031_tensorflow_yolov3\python\python.exe D:/20191031_tens ...

  4. 多线程执行sql报错处理

    pymysql多线程访问数据库报错:Packet sequence number wrong - got 7 expected 2 原文:https://www.cnblogs.com/heiao10 ...

  5. 关于MAC下pymysql连接mysql数据库报错2003的问题解决方法

    关于MAC下pymysql连接mysql数据库报错2003的问题解决方法 问题:pymysql.err.OperationalError: (2003, "Can't connect to ...

  6. Python连接mysql,插入数据时不报错,但是没有插入进去

    Python连接mysql,插入数据时不报错,但是没有插入进去在connect方法中,设置 autocommit =True conn=pymysql.connect(host=host_db,use ...

  7. Pycharm连接mysql数据库报错1130,1054

    Pycharm连接并创建mysql数据库报错 1.报错信息如下mysql.connector.errors.NotSupportedError: Authentication plugin 'cach ...

  8. Navicat链接数据库报错1130解决方案

    Navicat链接数据库报错1130解决方案 参考文章: (1)Navicat链接数据库报错1130解决方案 (2)https://www.cnblogs.com/newAndHui/p/113451 ...

  9. OpenStack在keystone部分同步数据库报错Errno 13解决办法

    OpenStack在keystone部分同步数据库报错Errno 13 在执行 su -s /bin/sh -c "keystone-manage db_sync" keyston ...

  10. mysql数据库报错1146_关于MySQL报错:[ERR] 1146

    最近因为电脑重装了系统,导致自己原本的数据库呗覆盖,需要重新重新安装数据库,但是由于我之前数据库版本是mysql 5.0.22,版本太低,所以小编决定安装mysql 5.7.23版本的,一开始没什么问 ...

最新文章

  1. Java内存模型深度剖析
  2. 笔记-高项案例题-2018年上-质量管理
  3. linux下的定时任务处理
  4. 关于linux的进程和线程
  5. mysql innodb数据结构_Mysql InnoDB数据结构
  6. macOS Docker 上安装、启动 MySQL
  7. 高效延时消息设计与实现的场景
  8. win10安装影子系统,导致电脑无限蓝屏,解决总结
  9. 系统架构设计笔记(91)—— 安全性规章
  10. VS2017安装WTL,解决创建WTL项目一直回弹问题
  11. SMS发送流程 Android2.2
  12. 解决克隆虚拟机后无法上网问题(亲测有效)
  13. Tensorflow2——Eager模式简介以及运用
  14. Java每日一练(4)
  15. 碳中和大潮惊涛拍岸,科技企业如何迈入这条大江大河?
  16. 【编译原理】交叉工具链详解
  17. 【密码算法 之七】GCM 浅析
  18. 爬虫:爬取糗事百科数据
  19. Java实验——定义一个类,该类中包含以下几个方法(静态):实现两个字符串数组的逆序排序,输出结果为字符串数组;求两个整形数组的交集;求两个浮点型数组的并集;
  20. java实现老鼠出迷宫

热门文章

  1. 爬虫---涨跌停股票池信息----(东方财富)
  2. JavaScript js 实现拖动窗口移动功能
  3. 刻录光驱不能访问出现函数不正确问题解决
  4. 什么是二级域名,什么是二级目录?
  5. android 系统时间不准确,小米手机时间不准确怎么调小米手机时间不准确怎样调...
  6. Windows7下chm文件打不开
  7. 使用豆瓣镜像安装Tensorflow
  8. 数据库的长连接和短链接
  9. 计算机 小学数学应用题教学设计,小学数学教案相遇问题应用题
  10. 数学建模--神经网络在线绘图工具,流程图绘图工具,OCR图片公式识别