开发一个网站或后台系统时,数据库是核心组成部分。每次用户登录、查询订单、提交表单,背后几乎都有一次数据库连接操作。但很多人只关注“连得上”,却忽略了“放得下”——连接用完后是否及时释放,直接关系到系统的稳定性和性能。
连接不释放,就像吃饭不买单
想象一下你在餐厅点餐,坐下后服务员给你递了菜单,你却一直占着座位刷手机,既不点菜也不走人。后面排队的人越来越多,餐厅很快就被堵满了。数据库连接池就像餐厅的座位,数量有限。如果程序打开了连接却没及时关闭,这些“占座不消费”的连接会迅速耗尽资源,新请求只能排队甚至失败。
常见场景:忘记关闭连接
在 Java 中使用 JDBC 是个典型例子。初学者常写出这样的代码:
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
System.out.println(rs.getString("name"));
}
// 忘记 close()
这段代码能运行,但每次执行都会留下一个未关闭的连接。短时间内可能看不出问题,一旦并发量上来,数据库就会报“Too many connections”错误。
正确做法:确保释放被执行
最稳妥的方式是使用 try-with-resources 语法,它能自动关闭实现了 AutoCloseable 接口的资源:
try (Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
while (rs.next()) {
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}
// 连接、语句、结果集都会自动关闭
即使中间发生异常,JVM 也会保证资源被释放。这种方式简洁且安全,避免了手动写 finally 块的繁琐和遗漏风险。
不只是 Java,其他语言也需注意
Python 使用 pymysql 或 psycopg2 时,也要记得关闭 cursor 和 connection:
import pymysql
conn = pymysql.connect(host='localhost', user='root', password='123', database='test')
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
results = cursor.fetchall()
for row in results:
print(row)
cursor.close()
conn.close() # 别忘了这句
更推荐用上下文管理器:
with pymysql.connect(...) as conn:
with conn.cursor() as cursor:
cursor.execute("SELECT * FROM users")
results = cursor.fetchall()
# 自动 commit,连接自动关闭
连接池不是万能的
很多人觉得用了 HikariCP、Druid 这类连接池就高枕无忧了。其实连接池只是优化了创建和复用,但它依然依赖你主动归还连接。如果你从池里借了连接却不 close(),池子迟早被掏空。close() 在连接池中并不是真正断开,而是把连接还回池中,供下次使用。
监控和排查建议
线上系统应配置数据库连接数监控。比如 MySQL 可通过 SHOW STATUS LIKE 'Threads_connected' 查看当前连接数。持续增长而不下降,基本可以判定有连接泄漏。配合应用日志,定位到具体代码模块后,重点检查是否有未关闭的连接操作。
一个小疏忽,可能在流量高峰时演变成服务雪崩。写代码时多花几秒钟写好资源释放,比半夜被报警叫醒强得多。