• python 数据库连接池DButils


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

    场景一:

    缺点:每次请求反复创建数据库连接,连接数太多
    复制代码
    import pymysql
    def 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 pymysql
    CONN=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 threading
    LOCK=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 PersistentDB
    import pymysql
    
    POOL = 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 threading
    
    for i in range(10):
        t = threading.Thread(target=func)
        t.start()
    复制代码

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

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

    复制代码
    import time
    import pymysql
    import threading
    from DBUtils.PooledDB import PooledDB, SharedDBConnection
    POOL = 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, '
    ')
    
        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, '
    ')
    
        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
    复制代码
  • 相关阅读:
    最全的CSS浏览器兼容问题整理
    游戏设计源概念——学习
    如何将数据从 Excel 导入到 SQL Server
    IE8网页显示不正常 用”兼容性视图”搞定
    Lua概念定义及相关资料
    谈谈主策划需要的能力
    微博营销实战经验总结
    创业公司CEO每天应该做的13件事
    2011营销往哪一个风向吹
    [转]网店博客营销之微博实战技巧:还没有做微博的掌柜看过来
  • 原文地址:https://www.cnblogs.com/Xanderzyl/p/11000335.html
Copyright © 2020-2023  润新知