2025-03-12
pyhton
00

目录

问题原因
1. 默认事务行为
2. 连接池连接占用
3. 业务实例增加导致压力骤增
解决方案
1. 通过配置连接池sesssion进行开启事务自动提交
2. 通过配置 maxconnections 设置最大允许的连接数
踩坑发现

背景

在生产环境中,我们使用了 pymysql 和 DBUtils.PooledDB 来管理 MySQL 数据库连接池。由于业务量激增,新增了多个业务实例,最终导致 MySQL 集群以及本地实例的连接数被打满,无法建立新的请求连接。经过排查,发现根本原因是 pymysql 默认开启事务,所有的 SQL 操作(包括查询)都会自动开启事务,并且不会自动提交,导致事务长时间占用连接,无法释放。

问题原因

1. 默认事务行为
  • pymysql 默认 autocommit=False: pymysql 默认不自动提交事务,也就是说,每执行一次 SQL 操作(包括查询)都会启动一个事务。如果在业务代码中没有显式调用 commit() 或 rollback(),那么这个事务就一直处于挂起状态。
2. 连接池连接占用
  • 事务不释放连接: 当一个连接处于挂起事务状态时,即使执行的是查询操作,该连接也不会被释放回连接池。随着大量任务的执行,长时间未提交的事务不断占用连接池中的连接,最终导致连接数打满。
3. 业务实例增加导致压力骤增
  • 新增业务实例后,同时发起的大量请求进一步加剧了连接数的压力,最终导致 MySQL 集群无法创建新的请求连接。

解决方案

1. 通过配置连接池sesssion进行开启事务自动提交
2. 通过配置 maxconnections 设置最大允许的连接数

配置 setsession=['SET AUTOCOMMIT=1'] 具体如下

dbpool = PooledDB( creator=pymysql, # 使用链接数据库的模块 maxconnections=50, # 连接池允许的最大连接数,0和None表示不限制连接数 mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建 maxcached=10, # 链接池中最多闲置的链接,0和None不限制 maxshared=0, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。 blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错 maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制 setsession=['SET AUTOCOMMIT=1'], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."] ping=1, # 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=db.dbhost, port=db.dbport, user=db.dbusername, password=db.dbpassword, database=db.dbdatabase, charset='utf8' )

踩坑发现

  • 无法通过conn.commit() 进行手动commit进行事务的自动提交

本文作者:曹子昂

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!