SvelteKit+Vite+MariaDB热重载时如何保留数据库连接池
解决SvelteKit热重载导致MariaDB连接池泄漏的问题
问题根源
Vite热重载时会重新加载服务器端模块,你原来的模块级单例变量(databaseConnectionPool)会被重置为undefined,每次热重载都会新建连接池,而旧连接池未被主动关闭,MariaDB的连接会一直保持,最终造成连接数持续增长。
解决方案
1. 用globalThis保存连接池实例(核心修复)
修改db.js,将连接池实例存储在Node.js的全局对象中,确保模块热重载时实例不会丢失:
import { createPool } from 'mariadb'; // 定义全局唯一的键名,避免和其他全局变量冲突 const GLOBAL_POOL_KEY = '__MARIADB_CONNECTION_POOL__'; export function createDatabaseConnectionPool() { // 检查全局对象中是否已有连接池实例 if (!globalThis[GLOBAL_POOL_KEY]) { globalThis[GLOBAL_POOL_KEY] = createPool({ connectionLimit: 5, // 你的连接池配置 host: 'localhost', user: 'your_user', password: 'your_password', database: 'your_db' }); // 监听进程退出事件,确保连接池被正确关闭 process.on('exit', async () => { await globalThis[GLOBAL_POOL_KEY].end(); }); // 处理Ctrl+C等中断信号,避免连接泄漏 process.on('SIGINT', async () => { await globalThis[GLOBAL_POOL_KEY].end(); process.exit(0); }); } return globalThis[GLOBAL_POOL_KEY]; }
2. 清理热重载时的旧模块连接池(可选)
如果你的旧代码中模块级变量仍持有连接池实例,可以添加Vite热重载的清理钩子,确保旧实例被关闭:
在db.js末尾添加:
if (import.meta.hot) { import.meta.hot.dispose(async () => { // 如果当前模块的旧连接池存在,且和全局实例不一致,关闭它 const localPool = /* 你原来的模块级变量名,比如databaseConnectionPool */; if (localPool && localPool !== globalThis[GLOBAL_POOL_KEY]) { await localPool.end(); } }); }
3. 验证修复效果
在hooks.server.js中添加日志,确认每次热重载返回的是同一个连接池实例:
import { createDatabaseConnectionPool } from '$lib/db.js'; const pool = createDatabaseConnectionPool(); console.log('当前连接池实例:', pool);
多次热重载后,日志中的实例应该是同一个对象,同时执行SHOW STATUS LIKE 'Threads_connected',连接数不会再随热重载增长。
原理说明
globalThis在Node.js的单个进程中是全局共享的,模块热重载不会重置全局对象的属性,因此连接池实例可以被持久化。- 主动监听进程退出和中断信号,确保应用关闭时连接池被
end()方法关闭,释放所有数据库连接。 - Vite的
import.meta.hot.dispose钩子会在模块被替换前触发,用来清理旧模块的资源,避免残留的连接池实例导致泄漏。
内容的提问来源于stack exchange,提问作者Abdelfattah Radwan
相关产品推荐
相关产品推荐

