当前位置:首页>python>Python操作数据库老是连接泄漏?记住这个上下文管理器写法,资源永远不会忘释放

Python操作数据库老是连接泄漏?记住这个上下文管理器写法,资源永远不会忘释放

  • 2026-10-11 06:46:58
Python操作数据库老是连接泄漏?记住这个上下文管理器写法,资源永远不会忘释放

我刚做Python开发那会,最烦的就是数据库连接泄漏。代码跑几分钟没问题,放生产环境跑一天,数据库连接池就炸了。我翻日志,全是“Too many connections”。后来我找到了一个写法,再也没出过这个问题。

核心原理就是Python的上下文管理器,也就是 with 语句。这个机制天生就是为了资源管理设计的。你打开一个文件,用with,文件会自动关闭。操作数据库也一样道理。

很多人写数据库连接代码是这样的:

conn = pymysql.connect(...) 

cursor = conn.cursor()

cursor.execute(sql)

data = cursor.fetchall()

conn.close()

我见过太多人只写了conn.close(),忘了cursor.close()。更常见的坑是代码中间抛了异常,后面关闭连接的代码根本没执行。连接就这么丢了。

用上下文管理器改写后,模样是这样:

from contextlib import contextmanager 

@contextmanager

def get_db():

   conn = pymysql.connect(host='localhost', user='root', password='123456', database='test')

   try:

       yield conn

   finally:

       conn.close()

使用的时候就简单了:

with get_db() as conn: 

   with conn.cursor() as cursor:

       cursor.execute('SELECT  FROM users')

       result = cursor.fetchall()

这里 yield 前面的代码是准备阶段,连接数据库。yield后面的代码是清理阶段,关闭连接。就算yield那一块代码报错了,finally里的conn.close()也一定会执行。这点我测试过无数次。

有人说这样写太啰嗦。你想想,你手动写关闭代码,每个函数都要写try...finally,还得记着把游标也关了。有一次我在循环里操作数据库,忘了关连接,循环几千次后数据库直接拒绝连接了。用contextmanager,一套写法到处用。

我还见过一种写法,直接在类里实现了__enter__和__exit__方法。那个也行,但不如这个装饰器版本简洁。我团队里新来的小伙子,看我这么写,五分钟就学会了。他原来写数据库代码每次都自己try...finally,经常漏掉游标的关闭。

分享一个我踩过的坑。有人把连接对象的创建放在了with块外面,只在with里yield了cursor。这样做有个问题,连接对象在with块结束后可能没被销毁。正确的做法是把连接对象和游标的创建都放在上下文管理器内部,外面只管用。我后来改成了嵌套写法,游标也在上下文管理器里处理:

@contextmanager 

def get_cursor():

   conn = pymysql.connect(...)

   cursor = conn.cursor()

   try:

       yield cursor

   finally:

       cursor.close()

       conn.close()

这个写法更保险。游标和连接一并关闭。生产环境跑了一个月,连接数一直稳定在个位数。之前手动管理的版本,连接数经常冲到一百多。

你写代码的时候,只要看到要手动close的资源,就直接套上上下文管理器。数据库连接、文件句柄、网络连接、甚至锁对象,都可以这么处理。我养成习惯后,再也没为资源泄漏发过愁。

最后说个细节,contextmanager这个装饰器来自contextlib模块,是标准库自带的。不用装任何第三方包。你导入就行。我遇到很多人不知道有这个模块,自己手写类实现上下文管理器协议,又累又容易错。

干开发这行的,谁没被数据库连接泄漏折磨过。这个写法不解决所有问题,但至少让你在打开资源的时候,不再担心忘记关闭。你每写一个数据库操作,都套上这个模式。连接泄漏,从此跟你没关系。

最新文章

随机文章