PyMySQL连接池

背景

  • 在用python写后端服务时候,需要与mysql数据库进行一些数据查询或者插入更新等操作。启动服务后接口运行一切正常,
    隔了第二天去看服务日志就会报错,问题如下:
pymysql.err.OperationalError: (2006, "MySQL server has gone away (BrokenPipeError(32, 'Broken pipe'))")
  • MySQL默认的wait_timeout时间28800秒,即8小时,超过8小时,MySQL就会放弃连接。MySQL默认设置的时间是8小时,可以通过运行show variables like ‘%timeout%’;这个SQL即可查看到,时间根据自己业务而定。
  • 对于数据库连接,一般不建议使用全局变量,在每次操作完成后即关闭连接。这是因为长时间保持数据库连接会对性能和资源消耗产生负面影响。与此相反,使用数据库连接池来维护和分配数据库连接是更好的做法。

好处

连接池的优点是可以在多个线程或进程之间共享,并且可以有效地管理连接数,而无需手动打开和关闭连接。

常用包

  • QueuePool 是 SQLAlchemy 内置的一个连接池实现,它可以管理一个连接队列,确保每个连接在使用后被适当地关闭。该池使用Python 自带的 queue 模块实现,并支持可配置的最大连接数、预处理语句等特性。优点是易于使用,无需其他依赖,并与SQLAlchemy 之间无缝集成。
  • PooledDB 是 DBUtils 库提供的一个连接池实现,可以与 SQLAlchemy 或其他 Python数据库库一起使用。它支持多种类型的连接池,并使用 threading模块实现线程安全,具有更高的性能和稳定性。该库还提供了一些方便的功能,例如自动回收空闲连接等。

pip install dbutils==1.3 

DBUtils python 代码

import os
import pymysql
import configparser
from DBUtils.PooledDB import PooledDB

def get_mysql_config():
	MODULE_REAL_DIR = os.path.dirname(os.path.realpath(__file__))
	
	config = configparser.ConfigParser()
	config.read(MODULE_REAL_DIR + '/../conf/config.conf')
	
	host = config.get('MYSQL_FORMAL_DB', '127.0.0.1')
	port = config.get('MYSQL_FORMAL_DB', '3306')
	user = config.get('MYSQL_FORMAL_DB', 'root')
	pwd = config.get('MYSQL_FORMAL_DB', 'mypwd')
	db = config.get('MYSQL_FORMAL_DB', 'mydb')
	return host, port,user, password, database
	
host, port, user, password, database = get_mysql_config()

class MySQLConnectionPool:

    def __init__(self,):
        self.pool = PooledDB(
            creator=pymysql,  # 使用链接数据库的模块
            mincached=10,  # 初始化时,链接池中至少创建的链接,0和None表示不创建
            maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
            maxconnections=200,  # 连接池允许的最大连接数,0和None表示不限制连接数
            maxusage=None,  # 一个链接最多被重复使用的次数,None表示无限制
            blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
            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=host,
            port=port,
            user=user,
            password=password,
            database=database,
            charset='utf8'
        )

    def open(self):
        self.conn = self.pool.connection()
        self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)  # 表示读取的数据为字典类型
        return self.conn, self.cursor

    def close(self, cursor, conn):
        cursor.close()
        conn.close()

    def select_one(self, sql, *args):
        """查询单条数据"""
        conn, cursor = self.open()
        cursor.execute(sql, args)
        result = cursor.fetchone()
        self.close(conn, cursor)
        return result

    def select_all(self, sql, args):
        """查询多条数据"""
        conn, cursor = self.open()
        cursor.execute(sql, args)
        result = cursor.fetchall()
        self.close(conn, cursor)
        return result

    def insert_one(self, sql, args):
        """插入单条数据"""
        self.execute(sql, args, isNeed=True)

    def insert_all(self, sql, datas):
        """插入多条批量插入"""
        conn, cursor = self.open()
        try:
            cursor.executemany(sql, datas)
            conn.commit()
            return {'result': True, 'id': int(cursor.lastrowid)}
        except Exception as err:
            conn.rollback()
            return {'result': False, 'err': err}

    def update_one(self, sql, args):
        """更新数据"""
        self.execute(sql, args, isNeed=True)

    def delete_one(self, sql, *args):
        """删除数据"""
        self.execute(sql, args, isNeed=True)

    def execute(self, sql, args, isNeed=False):
        """
        执行
        :param isNeed 是否需要回滚
        """
        conn, cursor = self.open()
        if isNeed:
            try:
                cursor.execute(sql, args)
                conn.commit()
            except:
                conn.rollback()
        else:
            cursor.execute(sql, args)
            conn.commit()
        self.close(conn, cursor)


"""
CREATE TABLE `names` (
  `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` VARCHAR(30) DEFAULT NULL COMMENT '姓名',
  `sex` VARCHAR(20) DEFAULT NULL COMMENT '性别',
  `age` int(5) DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='数据导入mysql';

"""


mysql = MySQLConnectionPool()

sql_insert_one = "insert into `names` (`name`, sex, age) values (%s,%s,%s)"
mysql.insert_one(sql_insert_one, ('唐三', '男', 25))

datas = [
    ('戴沐白', '男', 26),
    ('奥斯卡', '男', 26),
    ('唐三', '男', 25),
    ('小舞', '女', 100000),
    ('马红俊', '男', 23),
    ('宁荣荣', '女', 22),
    ('朱竹清', '女', 21),
]
sql_insert_all = "insert into `names` (`name`, sex, age) values (%s,%s,%s)"
mysql.insert_all(sql_insert_all, datas)

sql_update_one = "update `names` set age=%s where `name`=%s"
mysql.update_one(sql_update_one, (28, '唐三'))

sql_delete_one = 'delete from `names` where `name`=%s '
mysql.delete_one(sql_delete_one, ('唐三',))

sql_select_one = 'select * from `names` where `name`=%s'
results = mysql.select_one(sql_select_one, ('唐三',))
print(results)

sql_select_all = 'select * from `names` where `name`=%s'
results = mysql.select_all(sql_select_all, ('唐三',))
print(results)



目前最新版本是3.1.0, 官方文档: https://webwareforpython.github.io/DBUtils/main.html

模块中的类 PooledDB dbutils.pooled_db实现池 与数据库的稳定、线程安全的缓存连接,这些连接是透明的 使用任何 DB-API 2 数据库模块重用。

下图显示了当您 正在使用pooled_db连接:
在这里插入图片描述

PooledDB (pooled_db) 参数:

  • creator:返回新 DB-API 2 的任意函数 连接对象或符合 DB-API 2 的数据库模块

  • mincached :池中的初始空闲连接数 (默认值为 0 表示启动时未建立任何连接)

  • maxcached:池中的最大空闲连接数 (默认值 0 或 None 表示池大小不受限制)

  • MaxShared:允许的最大共享连接数 (默认值 0 或 None 表示所有连接都是专用的)。当达到此最大数量时,如果连接 已被要求为可共享。

  • maxconnections:通常允许的最大连接数 (默认值 0 或 None 表示任意数量的连接)

  • blocking:确定超过最大值时的行为。如果设置为 true,则阻止并等待 连接数减少,但默认情况下会报告错误。

maxusage:单个连接的最大重用次数 (默认值为 0 或 None 表示无限重用)。当达到连接的最大使用次数时, 连接将自动重置(关闭并重新打开)。

setsession:可用于 准备会话,例如 [“将 datestyle 设置为德语”,…]

reset:返回到池时应如何重置连接 (False 或 None 回滚以 begin() 开头的事务, 为了安全起见,默认值 True 始终发出回滚)

failures:可选的异常类或异常类的元组 应应用连接故障转移机制, 如果默认值 (OperationalError, InterfaceError,InternalError) 对于使用的数据库模块来说是不够的

ping:控制何时检查连接的可选标志 如果这样的方法可用,则使用 ping() 方法 (0 = 无 = 从不,1 = 默认值 = 从池中获取的时间,2 = 创建游标时,4 = 执行查询时,7 = 始终,以及这些值的所有其他位组合)

符合 DB-API 2 的 creator 函数或 connect 函数 指定为创建者的数据库模块将收到任何其他 主机、数据库、用户、密码等参数。你可以 在您自己的 creator 函数中选择部分或全部这些参数, 支持复杂的故障转移和负载平衡机制。


参考

Python连接MySQL数据库连接池
python DbUtils 封装
Python+Pymysql+PooledDB实现数据库连接池
Pymysql 连接池操作

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pymysqlPython用来连接MySQL数据库的一个库,而连接池则是一种管理数据库连接的技术,可以在多个请求之间共享数据库连接,提高了数据库访问效率。 pymysql连接池可以通过将已经建立好的连接存入一个池子,当有新的请求来临时,直接从池子获取连接,这样就避免了频繁创建、断开连接,从而提高了程序的性能。同时,连接池还可以设置最大连接数和最小连接数,当连接数达到最大值时,新的请求就会等待有连接被释放后再次获取。 pymysql连接池的使用方法如下: ``` import pymysql from DBUtils.PooledDB import PooledDB pool = PooledDB( creator=pymysql, # 使用pymysql作为数据库的链接库 maxconnections=6, # 最大连接数 mincached=2, # 初始化时连接池至少创建的空闲的链接,默认0 maxcached=5, # 链接池最多闲置的链接数,超过这个数后将被关闭,默认0 maxshared=3, # 链接池最多共享的链接数量,默认0 blocking=True, # 连接池如果没有可用连接后,是否阻塞等待。True表示等待,False表示不等待返回异常 maxusage=None, # 单个链接最多被使用次数,None表示无限制,默认None setsession=[], # 开始会话前执行的命令列表,默认为空 ping=0, # ping MySQL服务端,检查服务是否可用,默认为0表示不检查 host='127.0.0.1', port=3306, user='root', password='root', database='test' ) conn = pool.connection() # 从连接池获取一个连接 cursor = conn.cursor() sql = "SELECT * FROM table" cursor.execute(sql) result = cursor.fetchall() cursor.close() conn.close() # 注意:使用完后需要将连接释放回连接池 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值