连接池与DButils介绍
当我们在运用python通过HTTP连接与mysql这类数据库进行短暂连接时,因为可能就只是插入或者读取某些数据,所以读取完成后便关闭,并不需要考虑太多性能问题。但如果是在没有框架的情况下,在一个函数里用原生SQL通过比方说pymysql、Cmysql等模块频繁进行连接操作,那么效率就会很低,因为需要频繁验证HTTP服务,甚至可能会发生报错,由于线程可能发生堵塞,所以这里就有了数据库连接池的概念,通过它进行连接复用。
一说到数据库连接池,java 中有很多选择,C3P0、DBCP、Proxool、Tomcat-JDBC、druid 等等等等,五花八门,但python中似乎只有DBUtils。DBUtils 是一套允许线程化 Python 程序可以安全和有效的访问数据库的模块。DBUtils已经作为 Webware for Python 一部分用来结合 PyGreSQL 访问 PostgreSQL 数据库,当然他也可以用在其他Python应用程序中来访问 DB-API 2 兼容的数据库接口。
DBUtils接口与参数
DBUtils提供两种外部接口:
- PersistentDB :提供线程专用的数据库连接,并自动管理连接。
- PooledDB :提供线程间可共享的数据库连接,并自动管理连接。
一般常用的参数如下:
- creator: 可以生成 DB-API 2 连接的任何函数或 DB-API 2 兼容的数据库连接模块。
- mincached : 启动时开启的空连接数量(缺省值 0 意味着开始时不创建连接)
- maxcached: 连接池使用的最多连接数量(缺省值 0 代表不限制连接池大小)
- maxshared: 最大允许的共享连接数量(缺省值 0 代表所有连接都是专用的)如果达到了最大数量,被请求为共享的连接将会被共享使用。
- maxconnections: 最大允许连接数量(缺省值 0 代表不限制)
- blocking: 设置在达到最大数量时的行为(缺省值 0 或 False 代表返回一个错误;其他代表阻塞直到连接数减少)
- maxusage: 单个连接的最大允许复用次数(缺省值 0 或 False 代表不限制的复用)。当达到最大数值时,连接会自动重新连接(关闭和重新打开)
- setsession: 一个可选的SQL命令列表用于准备每个会话,如 [“set datestyle to german”, …]
- creator 函数或可以生成连接的函数可以接受这里传入的其他参数,例如主机名、数据库、用户名、密码等。你还可以选择传入creator函数的其他参数,允许失败重连和负载均衡。
python操作DBUtils:
这里话不多说,直接上代码,这里我们可以将所有配置写到一个文件里:
from DBUtils.PooledDB import PooledDB, SharedDBConnection
import pymysql
from django.http import JsonResponse
err = 'conerr'
db_config = {
"host": "localhost",
"port": "3306",
"user": "xxx",
"db": "moonkey2",
"password": "xxxxxx",
"charset":"utf8",
}
spool = PooledDB(pymysql,mincached=1, maxcached=50,maxshared=50,maxconnections=50, **db_config)
def mysql_seldic(sql):
""" 查询并返回全部数据方法(字典) """
try:
conn = spool.connection()
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 不对游标做任何处理就是元组
cursor.execute(sql)
row = cursor.fetchall()
cursor.close()
conn.close()
return row
except Exception as e:
return err.format(error=e)
def mysql_selrowid(sql):
"""查询并返回第一个字段数据方法(字典)"""
try:
conn = spool.connection()
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
rowcount = cursor.execute(sql)
fistcol = ''
if rowcount > 0:
row_1 = cursor.fetchone()
fistcol = row_1[0]
cursor.close()
conn.close()
return fistcol
except Exception as e:
return err.format(error=e)
def mysql_insert(sql):
"""插入新的数据"""
try:
conn = spool.connection()
cursor = conn.cursor()
effect_row = cursor.execute(sql)
conn.commit()
cursor.close()
conn.close()
return effect_row
except Exception as e:
return err.format(error=e)
def commit(sql):
'''事务'''
conn = spool.connection()
cursor = conn.cursor()
# uid = uuid.uuid1()
try:
# writetxt('sql','mysql_commit:'+ ' ' + str(datetime.datetime.now()) + ' ' +str(sql) + ' uid ' + str(uid) )
cursor.execute(sql)
cursor.close()
conn.commit()
return 1
except Exception as e:
# writetxt('sql','Exception:'+ ' ' + str(datetime.datetime.now()) + ' ' +str(e) )
conn.rollback()
return err
finally:
conn.close()
然后我们可以写调用函数去使用这些方法:
import Conn
#查询引导数据
def selguidanceinfo(guidanceinfoid):
sql = ' select gi.type,gi.guidanceinfoname,gi.guidanceinfoid from zy_guidanceinfo gi where gi.guidanceinfoid='+ str(guidanceinfoid) +' '
guidanceinfo = conn.mysql_seldic(sql)
return guidanceinfo
当然我还看到另一篇博客中用类来实例化属性的连接池,在这里记录一下:
class Mysql(object):
"""
MYSQL数据库对象,负责产生数据库连接 , 此类中的连接采用连接池实现获取连接对象:conn = Mysql.getConn()
释放连接对象;conn.close()或del conn
"""
#连接池对象
__pool = None
def __init__(self):
#数据库构造函数,从连接池中取出连接,并生成操作游标
self._conn = Mysql.__getConn()
self._cursor = self._conn.cursor()
@staticmethod
def __getConn():
"""
@summary: 静态方法,从连接池中取出连接
@return MySQLdb.connection
"""
if Mysql.__pool is None:
__pool = PooledDB(creator=MySQLdb, mincached=1 , maxcached=20 ,
host=Config.DBHOST , port=Config.DBPORT , user=Config.DBUSER , passwd=Config.DBPWD ,
db=Config.DBNAME,use_unicode=False,charset=Config.DBCHAR,cursorclass=DictCursor)
return __pool.connection()
def getOne(self,sql,param=None):
"""
@summary: 执行查询,并取出第一条
@param sql:查询SQL,如果有查询条件,请只指定条件列表,并将条件值使用参数[param]传递进来
@param param: 可选参数,条件列表值(元组/列表)
@return: result list/boolean 查询到的结果集
"""
if param is None:
count = self._cursor.execute(sql)
else:
count = self._cursor.execute(sql,param)
if count>0:
result = self._cursor.fetchone()
else:
result = False
这种我用上面那个函数同样进行了查询,基本效果都一样,如果要查看详细的增删改查代码可以移步他的博客。当然,我感觉这两种都不是代码的最精简版本,还可以做进一步封装,不过如果从可读性来讲,差不多了。