• Flask 的 数据库连接 与 DBUtils 数据库连接池


    Flask 的 数据库连接 与 DBUtils 数据库连接池

    本地线程:thread_local
    
                为每个线程创建存储数据的空间,用于线程之间的数据隔离
                否则多个线程同时访问,会使得数据混乱
    


    1 Flask 连接数据库 的方法

        pymysql
    
        依赖 SQLArchemy
    

    数据库连接的方案

    1  每次请求反复的创建数据库连接  (可以并发请求 -- 但是每次都会创建连接)
        # 连接数太多
    
    2  将连接放在全局变量  单线程(没有问题)  pymysql 只能支持单线程
        # 不能支持并发
    
    
    3  import threading
       LOCK = threading.RLock()
       with LOCK:
          connect ---
          ...
       # 这样控制每次只能有一个线程连接  (不能并发操作)
       # 不加锁 ,多线程状况下 会报错
    
    
    4  基于 DBUtils 实现 数据库连接池
    
        --  为每一个线程创建一个连接,当连接关闭的时候,不是真正的关闭;
            当本线程再次使用的时候,还是使用最开始的连接,
            只有当线程终止的时候,连接才会关闭
    
        -- 创建一个连接池(列表),为所有的线程 提供连接,使用完毕 再放回连接池
           所有的连接都可以被重复使用,共享
    
        -- 基于实现本地线程: 线程间的数据隔离
    


    2 为什么要使用数据库连接池

    如果没有连接池,使用pymysql来连接数据库时,单线程应用完全没有问题,但如果涉及到多线程应用那么就需要加锁,一旦加锁那么连接势必就会排队等待,当请求比较多时,性能就会降低了

        实现并发,避免无限制的连接
    


    3 使用DBUtils 数据库连接池

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

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

    基于本地线程   每个线程创建一个本地线程
    
    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 = POOL.connection(shareable=False)
        cursor = conn.cursor()
        cursor.execute('select * from tb1')
        result = cursor.fetchall()
        cursor.close()
        conn.close()                         # 结束本次连接,供本线程再次使用
    
    func()
    

    模式二:创建一批连接到连接池,供所有线程共享使用

    先 创建本地连接,让所有线程公用
    
    PS:由于pymysql、MySQLdb等threadsafety值为1,所以该模式连接池中的线程会被所有线程共享。
    所以 最大共享数 无用的。
    

    这种模式是最常使用的

    db.py:
    
        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'
        )
    
    
    使用的时候:
    
        from db import POOL
        
        def func():
            # 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常
            # 否则
            # 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。
            # 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。
            # 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到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()                         # 结束本次连接,把连接放回连接池
    
        func()
    

    小结

    1 每次操作都要连接  --- 不允许
    
    2 公用一个连接,多线程会出问题, 加锁-->> 变成了串行
    
    3 DBUtils 数据库连接池:
        (1) 基于treading.local 实现为每一个线程 创建一个连接,当前线程可以反复使用本线程的连接,线程终止的时候连接关闭
    
          # treading.local  为每一个线程创建一份独立的值,相当于一个字典(每个线程的唯一标识为 键)
    
        (2) 设置最大连接数
            默认启动时,连接池中创建的连接
    
          #  1个连接可以为3个线程服务(可能中途返还)
  • 相关阅读:
    !clrstack未显示任何方法名
    !dumpheap参数和SOS帮助系统的一些一般信息
    WinDbg Script---显示RCW对象引用的COM对象
    为什么不能将2个调试器附加到1个进程
    什么是互操作调试(Interop-Debugging)?
    为什么托管调试与本机调试不同?
    在WinDBG中管理源代码窗口
    如何从转储文件确定 /LARGEADDRESSAWARE
    Dump文件数据存储格式(九)
    Windbg命令系列---!cpuid(CPU信息)
  • 原文地址:https://www.cnblogs.com/big-handsome-guy/p/8544617.html
Copyright © 2020-2023  润新知