• Python 使用DBUtils 创建连接池解决多线程中连接丢失的问题


    转自:Python 使用 PyMysql、DBUtils 创建连接池,提升性能 和 python多线程操作数据库问题

    python多线程并发操作数据库,会存在链接数据库超时、数据库连接丢失、数据库操作超时等问题。

    解决方法:使用数据库连接池,并且每次操作都从数据库连接池获取数据库操作句柄,操作完关闭连接返回数据库连接池。

    *连接数据库需要设置charset = 'utf8', use_unicode = True,不然会报中文乱码问题

    *网上说解决python多线程并发操作数据库问题,连接时使用self.conn.ping(True)(检查并保持长连接),但是我这边亲测无法解决,建议还是使用数据库连接池

    解决方案:DBUtils

    Python 编程中可以使用 PyMysql 进行数据库的连接及诸如查询/插入/更新等操作,但是每次连接 MySQL 数据库请求时,都是独立的去请求访问,相当浪费资源,而且访问数量达到一定数量时,对 mysql 的性能会产生较大的影响。因此,实际使用中,通常会使用数据库的连接池技术,来访问数据库达到资源复用的目的。
    DBUtils 是一套 Python 数据库连接池包,并允许对非线程安全的数据库接口进行线程安全包装。DBUtils 来自 Webware for Python 。

    原理:

    在程序创建连接的时候,可以从一个空闲的连接中获取,不需要重新初始化连接,提升获取连接的速度
    关闭连接的时候,把连接放回连接池,而不是真正的关闭,所以可以减少频繁地打开和关闭连接

    python多线程代码:

    import threading
     
    class MyThread(threading.Thread):
     
        def __init__(self, name, count, exec_object):
            threading.Thread.__init__(self)
            self.name = name
            self.count = count
            self.exec_object = exec_object
     
        def run(self):
            while self.count >= 0:
                count = count - 1
                self.exec_object.execFunc(count)
     
    thread1 = MyThread('MyThread1', 3, ExecObject())
    thread2 = MyThread('MyThread2', 5, ExecObject())
    thread1.start()
    thread2.start()
    thread1.join() # join方法 执行完thread1的方法才继续主线程
    thread2.join() # join方法 执行完thread2的方法才继续主线程
    # 执行顺序 并发执行thread1 thread2,thread1和thread2执行完成才继续执行主线程
     
    # ExecObject类是自定义数据库操作的业务逻辑类
    # 
     
    ########join方法详解########
    thread1 = MyThread('MyThread1', 3, ExecObject())
    thread2 = MyThread('MyThread2', 5, ExecObject())
    thread1.start()
    thread1.join() # join方法 执行完thread1的方法才继续主线程
    thread2.start()
    thread2.join() # join方法 执行完thread2的方法才继续主线程
    # 执行顺序 先执行thread1,执行完thread1再执行thread2,执行完thread2才继续执行主线程

    mysql数据库连接池代码:

    import MySQLdb
    from DBUtils.PooledDB import PooledDB
     
    class MySQL:
     
        host = 'localhost'
        user = 'root'
        port = 3306
        pasword = ''
        db = 'testDB'
        charset = 'utf8'
     
        pool = None
        limit_count = 3 # 最低预启动数据库连接数量
     
        def __init__(self):
            self.pool = PooledDB(MySQLdb, self.limit_count, host = self.host, user = self.user, passwd = self.pasword, db = self.db,
                port = self.port, charset = self.charset, use_unicode = True)
     
        def select(self, sql):
            conn = self.pool.connection()
            cursor = conn.cursor()
            cursor.execute(sql)
            result = cursor.fetchall()
            cursor.close()
            conn.close()
            return result
     
        def insert(self, table, sql):
            conn = self.pool.connection()
            cursor = conn.cursor()
            try:
                cursor.execute(sql)
                conn.commit()
                return {'result':True, 'id':int(cursor.lastrowid)}
            except Exception as err:
                conn.rollback()
                return {'result':False, 'err':err}
            finally:
                cursor.close()
                conn.close()
     

    精简版的连接池例子

    import pymysql
    from DBUtils.PooledDB import PooledDB
    
    pool = PooledDB(pymysql,5,host='ip',user='user',passwd='passwd',db='db',port=3306,setsession=['SET AUTOCOMMIT = 1']) # 5为连接池里的最少连接数,setsession=['SET AUTOCOMMIT = 1']是用来设置线程池是否打开自动更新的配置,0为False,1为True
    conn = pool.connection() #以后每次需要数据库连接就是用connection()函数获取连接就好了
    cur=conn.cursor()
    SQL="select * from table"
    count=cur.execute(SQL)
    results=cur.fetchall()
    cur.close()
    conn.close()

    PooledDB 的参数:

    POOL = PooledDB(
    creator=pymysql, # 使用链接数据库的模块
    maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=5, # 链接池中最多闲置的链接,0和None不限制
    maxshared=1, # 链接池中最多共享的链接数量,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=’’,
    database=‘ziji’,
    charset=‘utf8’
    )

    在 uwsgi 中,每个 http 请求都会分发给一个进程,连接池中配置的连接数都是一个进程为单位的(即上面的最大连接数,都是在一个进程中的连接数),而如果业务中,一个 http 请求中需要的 sql 连接数不是很多的话(其实大多数都只需要创建一个连接),配置的连接数配置都不需要太大。

    连接池对性能的提升表现在:

    在程序创建连接的时候,可以从一个空闲的连接中获取,不需要重新初始化连接,提升获取连接的速度
    关闭连接的时候,把连接放回连接池,而不是真正的关闭,所以可以减少频繁地打开和关闭连接

    可能遇到的问题

    python3 安装 第三方库DBUtils安装成功 项目里却import不了的解决方案

    默认使用pip 下载的DBUtils是2.0版本的,但是python 3.0版本不能适配DBUtils 2.0 。所以解决方案是先卸载2.0的BUtils,然后pip 低版本的BUtils 。例如我pip install DBUtils==1.3 安装之后就好了。

     python3 mysql错误 pymysql.err.OperationalError: (2013, 'Lost connection to MySQL server during query')

    这个问题如果是在单线程中出现的,那可能是MySQL持久化链接保持时间为8小时(28800秒),过期后断开连。如果数据库没有新建连接,则会报此错。解决思路是调用前判断连接是否有效,如果有效继续,无效创建连接。

    参考:python3 mysql错误 pymysql.err.OperationalError: (2013, 'Lost connection to MySQL server during query')

    AttributeError: 'NoneType' object has no attribute 'read' 

    推测问题是,多线程操作数据库连接的时候,相互交叉释放了其他线程的连接。

    解决方案是在获取数据库连接时加锁,查询完毕后释放锁
    import threading
     
    lock = threading.Lock()
    
    lock.acquire()
    conn = pool.getConn()
    cur = conn.cursor()
    cur.execute(sql)
    rows = cur.fetchall()
    lock.release()

    转自:Python 使用 PyMysql、DBUtils 创建连接池,提升性能 和 python多线程操作数据库问题

  • 相关阅读:
    关于.net core https支持的整理
    数据库动态字段 个人设计的思考
    解决PowerDesigner中table的code长度限制和column的code长度限制
    Git内网服务器搭建
    解决.net core读取appSetting.json文件中文字符乱码
    excel 转换成pdf 总结
    .net生成荣誉证书
    Adobe Acrobat DC 安装
    swagger生成错误问题 汇总解决
    svn或git 提交文件排除
  • 原文地址:https://www.cnblogs.com/hi3254014978/p/15056127.html
Copyright © 2020-2023  润新知