Chinaunix首页 | 论坛 | 博客
  • 博客访问: 26268681
  • 博文数量: 2065
  • 博客积分: 10377
  • 博客等级: 上将
  • 技术积分: 21525
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-04 17:50
文章分类

全部博文(2065)

文章存档

2012年(2)

2011年(19)

2010年(1160)

2009年(969)

2008年(153)

分类: Python/Ruby

2010-02-06 12:10:06

python数据库连接池专题

昨天测试了一下开500个线程去请求数据库,不过这个时间不清楚会耗多少。即同时发起这么多的线程其效率会如何。于是想到是不是用数据库连接池技术可以明显改善一下这样的连接操作呢。呆会整理完了之后要测试一个数据:频繁建立与关闭数据库连接的效率与连接池之间的性能对比!

一、DBUtils模块学习

DBUtils实际上是一个包含两个子模块的Python包,一个用于连接DB-API 2模块,另一个用于连接典型的PyGreSQL模块。

PS:个人感觉 连接DB-API 2模块是不是讲的就是连接类似于MYSQL这样的API

全局的DB-API 2变量

SteadyDB.py

用于稳定数据库连接

PooledDB.py

连接池

PersistentDB.py

维持持续的数据库连接(持续性连接)

SimplePooledDB.py

简单连接池

PS:先摘抄DB-API出来一下吧

对标准DB-API 2模块的依赖如下图所示:

dbdep.gif

PS:从上图我们可以得知 如果用到了连接池与持久连接的话就需要用到稳定数据库连接。

 

安装

安装为顶层模块

python setup.py install

 

具体的模块学习:

DBUtils.SimplePooledDB 是一个非常简单的数据库连接池实现。他比完善的 PooledDB 模块缺少很多功能。 DBUtils.SimplePooledDB 本质上类似于 MiscUtils.DBPool 这个Webware的组成部分。你可以把它看作一种演示程序。

PS:从上图可以知道它是直接连接到DB-API

 

 

 

 

DBUtils.SteadyDB 是一个模块实现了"强硬"的数据库连接,基于DB-API 2建立的原始连接。一个"强硬"的连接意味着在连接关闭之后,或者使用次数操作限制时会重新连接。一个典型的例子是数据库重启时,而你的程序仍然在运行并需要访问数据库,或者当你的程序连接了一个防火墙后面的远程数据库,而防火墙重启时丢失了状态时。

一般来说你不需要直接使用 SteadyDB 它只是给接下来的两个模块提供基本服务, PersistentDB PooledDB

PS:这个模块一般不面对用户的。而只是提供基础服务之用的!)

DBUtils.PersistentDB 实现了强硬的、线程安全的、顽固的数据库连接,使用DB-API 2模块。如下图展示了使用 PersistentDB 时的连接层步骤:

DBUtils.PooledDB 实现了一个强硬的、线程安全的、有缓存的、可复用的数据库连接,使用任何DB-API 2模块。如下图展示了使用 PooledDB 时的工作流程:

(这两个模块是建立在SteadyDB 之上)

目前供我们选择的有两个模块:

PersistentDB PooledDB 都是为了重用数据库连接来提高性能,并保持数据库的稳定性。

所以选择何种模块,可以参考上面的解释。 PersistentDB 将会保持一定数量的连接供频繁使用。在这种情况下你总是保持固定数量的连接。如果你的程序频繁的启动和关闭线程,最好使用 PooledDB 。后面将会提到更好的调整,尤其在使用线程安全的DB-API 2模块时。

(两种连接池对象可以供选择!)

当然,这两个模块的接口是很相似的,你可以方便的在他们之间转换,并查看哪个更好一些。

为了使用 PersistentDB 你首先需要通过创建 PersistentDB 的实例来设置一个特定数据库连接的生成器,床底如下参数:

  • creator: 可以使用任意返回 DB-API 2 连接对象的函数活 DB-API 2 兼容的数据库模块。
  • maxusage: 一个连接最大允许复用次数(缺省为 0 False 意味着无限制的重用),当达到限制时,将会重新连接数据库
  • setsession: 一个可选的SQL命令列表可以用于准备会话,如 ["set datestyle to german", ...]
  • creator 函数或生成 DB-API 2 连接的函数,可以接受这里的附加参数,比如主机名、数据库、用户名、密码等等。你也可以选择传递给 creator 的其他参数,并允许提供失败重连和负载均衡。

 

 

 

 

为了使用 PooledDB 模块,你首先需要通过创建 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函数的其他参数,允许失败重连和负载均衡。

举个例子,如果你正在使用 pgdb 作为DB-API模块,并希望连接池中至少有5个连接到数据库 mydb

import pgdb # import used DB-API 2 module
from DBUtils.PooledDB import PooledDB
pool = PooledDB(pgdb, 5, database='mydb')

一旦设置好了连接池,你就可以按照如下请求一个连接:

db = pool.connection()

你可以使用这些连接有如原始的DB-API 2一样。而实际使用的是``SteadyDB``版本的强硬连接。

请注意连接可以与其他线程共享,只要你设置 maxshared 参数为非零,并且DB-API 2模块也允许。如果你想要使用专用连接则使用:

db = pool.connection(0)

如果你不再需要这个连接了,则可以返回给连接池使用 db.close() 。你也可以使用相同的方法获取另一个连接。

警告: 在一个多线程环境,不要使用下面的方法:

pool.connection().cursor().execute(...)
db = pool.connection()
cur = db.cursor()
cur.execute(...)
res = cur.fetchone()
cur.close() # or del cur
db.close() # or del db

 

 

二、示例 [方便我将来直接使用]

2.1 使用PersistentDB 模块

 

import threading,time,datetime

import MySQLdb

import DBUtils.PersistentDB

persist = DBUtils.PersistentDB.PersistentDB(MySQLdb,100,host='localhost',user='root',passwd='321',db='test',charset='utf8')

conn = persist.connection()

cursor = conn.cursor()

cursor.execute("insert into me values(1,'22222')")

conn.commit()

conn.close()

就可以得到数据库连接了!

  • creator: 可以使用任意返回 DB-API 2 连接对象的函数活 DB-API 2 兼容的数据库模块。
  • maxusage: 一个连接最大允许复用次数(缺省为 0 False 意味着无限制的重用),当达到限制时,将会重新连接数据库
  • setsession: 一个可选的SQL命令列表可以用于准备会话,如 ["set datestyle to german", ...]
  • creator 函数或生成 DB-API 2 连接的函数,可以接受这里的附加参数,比如主机名、数据库、用户名、密码等等。你也可以选择传递给 creator 的其他参数,并允许提供失败重连和负载均衡。

2.2 使用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函数的其他参数,允许失败重连和负载均衡。

#-*-coding:utf-8-*-

import threading,time,datetime

import MySQLdb

from DBUtils import PooledDB

pool = PooledDB.PooledDB(MySQLdb,10,50,100,500,False,host='localhost',user='root',passwd='321',db='test')

conn = pool.connection()

cursor = conn.cursor()

cursor.execute("insert into me values(1,'22222')")

conn.commit()

conn.close()

 

 

应用于多线程环境中使用!

示例:

#-*-coding:utf-8-*-

import threading,time,datetime

import MySQLdb

from DBUtils import PooledDB

pool = PooledDB.PooledDB(MySQLdb,100,50,100,490,False,host='localhost',user='root',passwd='321',db='test',charset='utf8')

默认打开的时候就创建了100个数据库连接。检查发现果然数据库中有100

class MyThread(threading.Thread):

    def __init__(self,threadName):

        self.conn = pool.connection()  直接从数据库连接池中提取

        threading.Thread.__init__(self,name=threadName)

 

   

    def run(self):

        cursor=self.conn.cursor()

        print "hello--->",self.getName()

        file_objct = open('8.txt','a+')

        file_objct.write(self.getName()+'\n')

        file_objct.close()

        #cursor.execute("call loaddate();")

        #self.conn.commit()

        time.sleep(10)

   

    def __del__(self):

        self.conn.close()

        self.conn = None      

 

for i in range(5):

    obj = MyThread(str(i))

    obj.start()

 

 

  • 如果我开480个线程的话 数据库显示的正是480个连接!maxconnections: 最大允许连接数量(缺省值 0 代表不限制)

 

如果我现在将代码调整如下:

#-*-coding:utf-8-*-

import threading,time,datetime

import MySQLdb

from DBUtils import PooledDB

pool = PooledDB.PooledDB(MySQLdb,100,50,100,400,False,host='localhost',user='root',passwd='321',db='test',charset='utf8')

class MyThread(threading.Thread):

    def __init__(self,threadName):

        self.conn = pool.connection()

        threading.Thread.__init__(self,name=threadName)

 

   

    def run(self):

        cursor=self.conn.cursor()

        print "hello--->",self.getName()

        file_objct = open('8.txt','a+')

        file_objct.write(self.getName()+'\n')

        file_objct.close()

        #cursor.execute("call loaddate();")

        #self.conn.commit()

        time.sleep(10)

   

    def __del__(self):

        self.conn.close()

        self.conn = None      

 

for i in range(402):

    obj = MyThread(str(i))

    obj.start()

连接池最大的数目才 400  现在我想要创建 402 肯定报错了!

 

 

 

阅读(1106) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~