共计 3295 个字符,预计需要花费 9 分钟才能阅读完成。
DBUtils 是一个允许在多线程 python 应用和数据库之间安全及高效连接的 python 模块套件。
模块
DBUtils 套件包含两个模块子集,一个适用于兼容 DB-API 2 接口的模块,一个适用于 PyGreSQL 的模块。
Universal DB-API 2 variant 该子集下的模块依赖关系如图:
Classic PyGreSQL variant 该子集下的模块依赖关系如图:
SimplePooledDB
DBUtils.SimplePooledDB 是池化数据库连接中非常基础的一种实现。相较于 PooledDB,它并不那么复杂,且缺少 failover 机制。SimplePooledDB 应视为一种概念演示,不要直接在生产环境使用。
SteadyDB
DBUtils.SteadyDB 基于兼容 DB-API 2 接口的数据库模块创建的普通连接,实现了 ” 加强 ” 连接。具体指当数据库连接关闭、丢失或使用频率超出限制时,将自动重新获取连接。
典型的应用场景如下:在某个维持了某些数据库连接的程序运行时重启了数据库,或在某个防火墙隔离的网络中访问远程数据库时重启了防火墙。
PersistentDB
DBUtils.PersistentDB 实现了稳定,线程仿射(thread-affine),持久化的数据库连接。下图显式了使用 PersistentDB 进行连接时涉及的连接层:
某个线程第一次开启一个数据库连接时,该连接将用于此特定线程。即使在线程中关闭连接,连接也会保持打开状态,以便同一个线程的下一次连接请求直接使用。线程结束时该连接会自动关闭。
简而言之:PersistentDB 会回收数据库连接从而在整体上增加多线程应用的数据库访问性能,它确保线程之间永远不会共享连接。
因此即使底层的 DB-API 模块不是 connection 级别的线程安全,PersistentDB 也可以完美实现线程安全,避免在其他线程更改数据库会话或执行跨多个 SQL 指令的事务时出现问题。
要使用 PersistentDB 模块,首先传递以下参数创建 PersistentDB 实例:
creator:兼容 DB-API 2 的数据库模块或返回 DB-API 2 连接的任意函数
maxusage:单个连接的最大重用次数(0 或 None 表示无重用次数限制),达到该限制后自动关闭并重新打开连接
setsession:设置连接会话的 sql 指令列表,比如[“set wait_timeout = 100″, …]
failures:异常类或异常类元组。在默认的 (OperationalError, InternalError) 不能处理连接 failover 机制时使用
ping:如果 ping()方法可用,该值表示何时使用 ping()方法检查连接(0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always, and all other bit combinations of these values)
closeable:如果设置为 True,将允许手动 close()连接,默认为 False,忽略关闭连接的操作,只在线程终止时自动关闭
threadlocal:表示 thread-local 数据的类。设置值为 threading.local 可能获取连接的速度更快,但不一定适用于所有情况(例如,mod_wsgi 会清空 requests 之间的 threading.local 数据)
传递给 creator 参数值创建 connection 对象的参数,如 host, database 等
import pymysql
from DBUtils.PersistentDB import PersistentDB
persist = PersistentDB(creator=pymysql, user=”root”, passwd=”123456″, db=”test”)
# conn 的使用和常规 DB-API 2 接口类似
conn = persist.connection()
NOTE: 需要在连接上调用 begin()方法明确开启事务。这可以确保 a. 只在事务完成时才重新打开连接 b. 连接被同一个线程重用时回滚。
PooledDB
DBUtils.PooledDB 实现了稳定、线程安全的缓存连接池。下图显式了使用 PooledDB 进行连接时涉及的连接层:
使用正整数的 maxshared 参数和 connection 级别的线程安全的 creator 参数创建连接池时,连接池中的连接默认是线程间共享的。但仍可以请求非线程共享的专用数据库连接。
除了共享连接池外,还可以创建至少 mincached 个,至多 maxcached 个连接的空闲连接池,在共享连接池未满 (不太理解) 或线程请求专用数据库连接时使用。当某个线程关闭不再共享的连接时,该连接将回收到到空闲连接池以便再次。
如果底层的 DB-API 2 模块非线程安全,将使用线程锁确保 PooledDB 连接是线程安全的。但对于线程专用的连接,要小心更改数据库会话或执行跨多个 SQL 指令的事务带来的不良影响。
要使用 PoolDB 模块,首先传递以下参数创建 PoolDB 实例:
creator:同 PersistentDB
mincached:连接池中创建的初始连接数(0 表示初始不创建)
maxcached:连接池中允许的最大空闲连接数(0 或 None 表示无限制)
maxshared:允许的最大共享连接数(0 或 None 表示所有连接都是专用的),When this maximum number is reached, connections are shared if they have been requested as shareable
maxconnections:允许的最大连接数(0 或 None 表示无限制)
blocking:查过最大值是否阻塞。True 表示将阻塞直到释放新的连接,默认 False 表示抛出异常
maxusage:同 PersistentDB
setsession:同 PersistentDB
reset:返回连接池时应该怎样重置连接 (False 或 None 将只回滚明确调用了 begin() 开启的事务,默认值为 True,出于安全考虑总是会回滚)
failures:同 PersistentDB
ping:同 PersistentDB
传递给 creator 参数值创建 connection 对象的参数,如 host, database 等
import pymysql
from DBUtils.PooledDB import PooledDB
pool = PooledDB(creator=pymysql, 5, user=”root”, passwd=”123456″, db=”test”)
# conn 的使用和常规 DB-API 2 接口类似
conn = pool.connection()
对于线程共享的连接池,可以用以下方式获取线程专用连接:
conn = pool.connection(shareable=False)
# 或者
conn = pool.dedicated_connection()
对于不再使用的连接,调用 close()方法回收到连接池。
在多线程环境中,不要写以下代码,这会导致连接过早释放并被其他线程重用,如果连接非线程安全可能导致程序出现严重错误:
pool.connection().cursor().execute(…)
NOTE: 需要在连接上调用 begin()方法明确开启事务。这可以确保 a. 只在事务完成时才重新打开连接 b. 连接在返回连接池之前执行回滚 c. 连接不会被其他线程共享
如何选择
PooledDB 和 PersistentDB 都通过回收数据库连接,且即使数据库连接中断也能保持稳定性的方式从而达到提升数据库访问性能的目的。在现实场景中应该如何选择呢?对于保持常量线程数且频繁使用数据库的应用,使用 PersistentDB;对于频繁开启、结束线程的应用,使用 PooledDB。
其他
如果程序中使用了 ORM 框架,如 SQLObject 或 SQLAlchemy,不需要使用 DBUtils,因为这些框架自身维护了连接池。