Python 使用DBUtils 创建连接池解决多线程中连接丢失的问题

  • A+
所属分类:Python

转自:Python 使用 PyMysql、DBUtils 创建连接池,提升性能 和 python多线程操作数据库问题

python多线程并发操作数据库,会存在链接数据库超时、数据库连接丢失、数据库操作超时等问题。

解决方法:使用数据库连接池,并且每次操作都从数据库连接池获取数据库操作句柄,操作完关闭连接返回数据库连接池。

*连接数据库需要设置charset = 'utf8', use_unicode = True,不然会报中文乱码问题

*网上说解决python多线程并发操作数据库问题,连接时使用self.conn.ping(True)(检查并保持长连接),但是我这边亲测无法解决,建议还是使用数据库连接池

解决方案:DBUtils

Python 编程中可以使用 PyMysql 进行数据库的连接及诸如查询/插入/更新等操作,但是每次连接 MySQL 数据库请求时,都是独立的去请求访问,相当浪费资源,而且访问数量达到一定数量时,对 mysql 的性能会产生较大的影响。因此,实际使用中,通常会使用数据库的连接池技术,来访问数据库达到资源复用的目的。
DBUtils 是一套 Python 数据库连接池包,并允许对非线程安全的数据库接口进行线程安全包装。DBUtils 来自 Webware for Python 。

原理:

在程序创建连接的时候,可以从一个空闲的连接中获取,不需要重新初始化连接,提升获取连接的速度
关闭连接的时候,把连接放回连接池,而不是真正的关闭,所以可以减少频繁地打开和关闭连接

python多线程代码:

  1. class MyThread(threading.Thread):

  2. def __init__(self, name, count, exec_object):

  3. threading.Thread.__init__(self)

  4. self.count = count

  5. self.exec_object = exec_object

  6. while self.count >= 0:

  7. count = count - 1

  8. self.exec_object.execFunc(count)

  9. thread1 = MyThread('MyThread1', 3, ExecObject())

  10. thread2 = MyThread('MyThread2', 5, ExecObject())

  11. thread1.join() # join方法 执行完thread1的方法才继续主线程

  12. thread2.join() # join方法 执行完thread2的方法才继续主线程

  13. # 执行顺序 并发执行thread1 thread2,thread1和thread2执行完成才继续执行主线程

  14. # ExecObject类是自定义数据库操作的业务逻辑类

  15. thread1 = MyThread('MyThread1', 3, ExecObject())

  16. thread2 = MyThread('MyThread2', 5, ExecObject())

  17. thread1.join() # join方法 执行完thread1的方法才继续主线程

  18. thread2.join() # join方法 执行完thread2的方法才继续主线程

  19. # 执行顺序 先执行thread1,执行完thread1再执行thread2,执行完thread2才继续执行主线程

mysql数据库连接池代码:

  1. from DBUtils.PooledDB import PooledDB

  2. limit_count = 3 # 最低预启动数据库连接数量

  3. self.pool = PooledDB(MySQLdb, self.limit_count, host = self.host, user = self.user, passwd = self.pasword, db = self.db,

  4. port = self.port, charset = self.charset, use_unicode = True)

  5. def select(self, sql):

  6. conn = self.pool.connection()

  7. cursor = conn.cursor()

  8. cursor.execute(sql)

  9. result = cursor.fetchall()

  10. def insert(self, table, sql):

  11. conn = self.pool.connection()

  12. cursor = conn.cursor()

  13. cursor.execute(sql)

  14. conn.commit()

  15. return {'result':True, 'id':int(cursor.lastrowid)}

  16. except Exception as err:

  17. conn.rollback()

  18. return {'result':False, 'err':err}

  19. cursor.close()

精简版的连接池例子

PooledDB 的参数:

POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=1, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:[“set datestyle to …”, “set time zone …”]
ping=0,
# ping MySQL服务端,检查是否服务可用。
# 如:0 = None = never,
# 1 = default = whenever it is requested,
# 2 = when a cursor is created,
# 4 = when a query is executed,
# 7 = always
host=‘127.0.0.1’,
port=3306,
user=‘root’,
password=’’,
database=‘ziji’,
charset=‘utf8’
)

在 uwsgi 中,每个 http 请求都会分发给一个进程,连接池中配置的连接数都是一个进程为单位的(即上面的最大连接数,都是在一个进程中的连接数),而如果业务中,一个 http 请求中需要的 sql 连接数不是很多的话(其实大多数都只需要创建一个连接),配置的连接数配置都不需要太大。

连接池对性能的提升表现在:

在程序创建连接的时候,可以从一个空闲的连接中获取,不需要重新初始化连接,提升获取连接的速度
关闭连接的时候,把连接放回连接池,而不是真正的关闭,所以可以减少频繁地打开和关闭连接

python3 安装 第三方库DBUtils安装成功 项目里却import不了的解决方案

默认使用pip 下载的DBUtils是2.0版本的,但是python 3.0版本不能适配DBUtils 2.0 。所以解决方案是先卸载2.0的BUtils,然后pip 低版本的BUtils 。例如我pip install DBUtils==1.3 安装之后就好了。

这个问题如果是在单线程中出现的,那可能是MySQL持久化链接保持时间为8小时(28800秒),过期后断开连。如果数据库没有新建连接,则会报此错。解决思路是调用前判断连接是否有效,如果有效继续,无效创建连接。

转自:Python 使用 PyMysql、DBUtils 创建连接池,提升性能 和 python多线程操作数据库问题

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin