博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python 数据库连接池DButils
阅读量:5165 次
发布时间:2019-06-13

本文共 5206 字,大约阅读时间需要 17 分钟。

常规的数据库链接存在的问题:

场景一:

缺点:每次请求反复创建数据库连接,连接数太多
import pymysqldef index():    conn = pymysql.connect()    cursor = conn.cursor()    cursor.execute('select * from tb where id > %s',[5,])    result = cursor.fetchall()    cursor.close()    conn.close()    print(result)        def update():    # 第一步:缺点:每次请求反复创建数据库连接,连接数太多    conn = pymysql.connect()    cursor = conn.cursor()    cursor.execute('update userinfo set username=%s where id > %s',['ctz',5,])    conn.commit()    cursor.close()    conn.close()    return  'hello'

存在问题:每一次请求就得创建数据库链接,可能我们处理数据只需要很少时间,而连接数据库却占了很长时间,每次请求反复创建数据库连接,连接数太多,造成数据库性能的损耗

 

场景二:

缺点,不能支持并发
import pymysqlCONN=pymysql.connect()def index():    cursor = CONN.cursor()    cursor.execute('select * from tb where id > %s',[5,])    result = cursor.fetchall()    cursor.close()    CONN.close()    print(result)def update():    cursor = CONN.cursor()    cursor.execute('update userinfo set username=%s where id > %s',['ctz',5,])    CONN.commit()    cursor.close()    CONN.close()    return  'hello'

把数据库链接放到常量中去了,这样就可以保证每次请求都只创建一次数据库链接,但是还是存在问题:不能支持并发

  如果有多个线程进来的话要执行同一个函数,第一个线程还没执行玩,第二个线程进来啦,但是只有一个conn这时就报错;当然我们也可以用下面这种方式解决报错问题:

import threadingLOCK=threading.RLock()CONN=pymysql.connect()def index():    with LOCK:        cursor = CONN.cursor()        cursor.execute('select * from tb where id > %s', [5, ])        result = cursor.fetchall()        cursor.close()            print(result)

但是 这样虽然解决了多个线程竞争报错的问题,但是程序加锁后变成了串行,运行效率就变低了

 

为了解决上面两个场景出现的问题,我们找到了解决的办法,就是创建数据库连接池

 

数据库链接池

DBUtils是Python的一个用于实现数据库连接池的模块。

此连接池有两种连接模式:

  • 模式一:为每个线程创建一个连接,线程即使调用了close方法,也不会关闭,只是把连接重新放到连接池,供自己线程再次使用。当线程终止时,连接自动关闭。

   实现原理:基于threaing.local实现为每一个线程创建一个链接,该线程关闭时,不是真正关闭;本线程再次调用时,还是使用的最开始创建的连接。直到线程终止,数据库连接才关闭

"""为每个线程创建一个连接,thread.local实现。"""from DBUtils.PersistentDB import PersistentDBimport pymysqlPOOL = PersistentDB(    creator=pymysql,  # 使用链接数据库的模块    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    closeable=False,    # 如果为False时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。如果为True时, conn.close()则关闭链接,那么再次调用pool.connection时就会报错,因为已经真的关闭了连接(pool.steady_connection()可以获取一个新的链接)    threadlocal=None,  # 本线程独享值得对象,用于保存链接对象,如果链接对象被重置    host='127.0.0.1',    port=3306,    user='root',    password='123',    database='pooldb',    charset='utf8')def func():    # conn = SteadyDBConnection()    conn = POOL.connection()    cursor = conn.cursor()    cursor.execute('select * from tb1')    result = cursor.fetchall()    cursor.close()    conn.close() # 不是真的关闭,而是假的关闭。 conn = pymysql.connect()   conn.close()    conn = POOL.connection()    cursor = conn.cursor()    cursor.execute('select * from tb1')    result = cursor.fetchall()    cursor.close()    conn.close()import threadingfor i in range(10):    t = threading.Thread(target=func)    t.start()

模式二:创建一批连接到连接池,供所有线程共享使用,使用时来进行获取,使用完毕后,再次放回到连接池。

PS:由于pymysql、MySQLdb等threadsafety值为1,所以该模式连接池中的线程会被所有线程共享。

import timeimport pymysqlimport threadingfrom DBUtils.PooledDB import PooledDB, SharedDBConnectionPOOL = PooledDB(    creator=pymysql,  # 使用链接数据库的模块    maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建    maxcached=5,  # 链接池中最多闲置的链接,0和None不限制    maxshared=3,  # 链接池中最多共享的链接数量,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='123',    database='pooldb',    charset='utf8')def func():    # 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常    # 否则    # 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。    # 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。    # 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。    # 一旦关闭链接后,连接就返回到连接池让后续线程继续使用。    # PooledDedicatedDBConnection    conn = POOL.connection()    # print(th, '链接被拿走了', conn1._con)    # print(th, '池子里目前有', pool._idle_cache, '\r\n')    cursor = conn.cursor()    cursor.execute('select * from tb1')    result = cursor.fetchall()    conn.close()    conn = POOL.connection()    # print(th, '链接被拿走了', conn1._con)    # print(th, '池子里目前有', pool._idle_cache, '\r\n')    cursor = conn.cursor()    cursor.execute('select * from tb1')    result = cursor.fetchall()    conn.close()func()

PS:

如果有三个线程来连接池中获取链接:   1个链接可以为三个线程提供服务   2个链接可以为三个线程提供服务   3个链接可以为三个线程提供服务maxshared在使用pymysql/mysqldb时均无用,因为他们的threadsafety都是1,而查看PooledDB源码就可以知道        if threadsafety > 1 and maxshared:            self._maxshared = maxshared            self._shared_cache = []  # the cache for shared connections        else:            self._maxshared = 0即使设置了 永远都是0

 

转载于:https://www.cnblogs.com/Xanderzyl/p/11000335.html

你可能感兴趣的文章
TCP/IP协议原理与应用笔记24:网际协议(IP)之 IP协议的简介
查看>>
SAP HANA开发中常见问题- 基于SAP HANA平台的多团队产品研发
查看>>
游戏中的心理学(一):认知失调有前提条件
查看>>
WHAT I READ FOR DEEP-LEARNING
查看>>
【Ruby】Ruby在Windows上的安装
查看>>
Objective C 总结(十一):KVC
查看>>
BZOJ 3747 洛谷 3582 [POI2015]Kinoman
查看>>
vue实战(7):完整开发登录页面(一)
查看>>
Visual Studio自定义模板(二)
查看>>
【Mood-20】滴滤咖啡做法 IT工程师加班必备 更健康的coffee 项目经理加班密鉴
查看>>
读《构建之法-软件工程》第四章有感
查看>>
使用 Printf via SWO/SWV 输出调试信息
查看>>
.net 分布式架构之分布式锁实现(转)
查看>>
吴恩达机器学习笔记 —— 3 线性回归回顾
查看>>
Problem E: Automatic Editing
查看>>
SpringBoot 使用 MyBatis 分页插件 PageHelper 进行分页查询
查看>>
《DSP using MATLAB》Problem 6.17
查看>>
微信公众平台开发实战Java版之如何网页授权获取用户基本信息
查看>>
一周TDD小结
查看>>
sizeof与strlen的用法
查看>>