Sequelize 数据库连接实战 - 连接池配置与最佳实践
发布时间:2024-01-15
作者:一介布衣
标签:Sequelize, 数据库连接, 连接池, 性能优化
前言
上一篇文章我们搭建好了 Sequelize 的开发环境,今天咱们来深入了解数据库连接的各种姿势。说实话,数据库连接这块看似简单,但里面的门道还真不少。
我记得刚开始用 Sequelize 的时候,经常遇到连接超时、连接池耗尽等问题,特别是在高并发场景下,这些问题更加明显。经过一段时间的摸索和踩坑,总结了一些实用的经验,今天分享给大家。
连接方式详解
Sequelize 提供了多种数据库连接方式,我们来逐一了解。
1. 连接字符串方式
这是最简单直接的方式:
javascript
const { Sequelize } = require('sequelize');
// SQLite
const sequelize = new Sequelize('sqlite::memory:');
// 或者文件数据库
const sequelize = new Sequelize('sqlite:./database.sqlite');
// PostgreSQL
const sequelize = new Sequelize('postgres://user:password@localhost:5432/dbname');
// MySQL
const sequelize = new Sequelize('mysql://user:password@localhost:3306/dbname');
优点:配置简单,一行搞定
缺点:不够灵活,难以进行高级配置
2. 参数对象方式
这是我推荐的方式,配置更加灵活:
javascript
const sequelize = new Sequelize({
dialect: 'postgres',
host: 'localhost',
port: 5432,
database: 'myapp',
username: 'postgres',
password: 'password',
// 连接池配置
pool: {
max: 10,
min: 0,
acquire: 30000,
idle: 10000
},
// 其他配置
logging: false,
timezone: '+08:00'
});
3. 分离配置方式
在实际项目中,我喜欢把配置单独抽出来:
javascript
// config/database.js
const config = {
development: {
dialect: 'sqlite',
storage: './dev-database.sqlite',
logging: console.log
},
test: {
dialect: 'sqlite',
storage: ':memory:',
logging: false
},
production: {
dialect: 'postgres',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
username: process.env.DB_USER,
password: process.env.DB_PASS,
pool: {
max: 20,
min: 5,
acquire: 60000,
idle: 10000
},
logging: false
}
};
const env = process.env.NODE_ENV || 'development';
module.exports = config[env];
连接池深度解析
连接池是 Sequelize 性能优化的关键,我们来详细了解一下。
连接池参数详解
javascript
pool: {
max: 10, // 最大连接数
min: 0, // 最小连接数
acquire: 30000, // 获取连接的最大等待时间(毫秒)
idle: 10000, // 连接空闲多久后释放(毫秒)
evict: 1000, // 检查空闲连接的间隔时间
handleDisconnects: true // 自动处理断开的连接
}
不同场景的连接池配置
小型应用(日活 < 1000)
javascript
pool: {
max: 5,
min: 1,
acquire: 30000,
idle: 10000
}
中型应用(日活 1000-10000)
javascript
pool: {
max: 15,
min: 3,
acquire: 60000,
idle: 10000
}
大型应用(日活 > 10000)
javascript
pool: {
max: 30,
min: 10,
acquire: 60000,
idle: 5000
}
连接池监控
我写了一个简单的连接池监控函数:
javascript
function monitorConnectionPool(sequelize) {
const pool = sequelize.connectionManager.pool;
setInterval(() => {
console.log('连接池状态:', {
总连接数: pool.size,
可用连接: pool.available,
使用中连接: pool.using,
等待连接: pool.waiting
});
}, 10000); // 每10秒检查一次
}
// 使用
monitorConnectionPool(sequelize);
连接测试与健康检查
基础连接测试
javascript
async function testConnection() {
try {
await sequelize.authenticate();
console.log('✅ 数据库连接正常');
return true;
} catch (error) {
console.error('❌ 数据库连接失败:', error.message);
return false;
}
}
高级健康检查
javascript
async function healthCheck() {
const startTime = Date.now();
try {
// 测试连接
await sequelize.authenticate();
// 测试查询性能
const [results] = await sequelize.query('SELECT 1 as test');
const responseTime = Date.now() - startTime;
return {
status: 'healthy',
responseTime: `\${responseTime}ms`,
database: sequelize.getDialect(),
timestamp: new Date().toISOString()
};
} catch (error) {
return {
status: 'unhealthy',
error: error.message,
timestamp: new Date().toISOString()
};
}
}
// 定期健康检查
setInterval(async () => {
const health = await healthCheck();
console.log('数据库健康状态:', health);
}, 30000);
多数据库连接管理
在一些复杂的项目中,可能需要连接多个数据库:
javascript
// connections/index.js
const { Sequelize } = require('sequelize');
// 主数据库
const mainDB = new Sequelize({
dialect: 'postgres',
host: 'main-db-host',
database: 'main_db',
username: 'user',
password: 'pass'
});
// 日志数据库
const logDB = new Sequelize({
dialect: 'mysql',
host: 'log-db-host',
database: 'log_db',
username: 'user',
password: 'pass'
});
// 缓存数据库
const cacheDB = new Sequelize({
dialect: 'sqlite',
storage: './cache.sqlite'
});
module.exports = {
mainDB,
logDB,
cacheDB
};
使用时:
javascript
const { mainDB, logDB } = require('./connections');
// 在主数据库中查询用户
const user = await mainDB.models.User.findByPk(1);
// 在日志数据库中记录操作
await logDB.models.Log.create({
action: 'user_query',
userId: user.id,
timestamp: new Date()
});
常见连接问题解决
1. 连接超时
现象:SequelizeConnectionAcquireTimeoutError
原因:连接池耗尽或数据库响应慢
解决方案:
javascript
// 增加连接池大小和超时时间
pool: {
max: 20,
acquire: 60000
}
// 或者优化查询,减少长时间占用连接
2. 连接断开
现象:SequelizeConnectionError: Connection lost
解决方案:
javascript
// 启用自动重连
pool: {
handleDisconnects: true,
retry: {
max: 3
}
}
// 或者手动重连
sequelize.connectionManager.initPools();
3. SSL 连接问题
现象:SSL 相关错误
解决方案:
javascript
// PostgreSQL SSL 配置
const sequelize = new Sequelize({
dialect: 'postgres',
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false // 开发环境可以设为 false
}
}
});
// MySQL SSL 配置
const sequelize = new Sequelize({
dialect: 'mysql',
dialectOptions: {
ssl: {
ca: fs.readFileSync('./ssl/ca-cert.pem'),
key: fs.readFileSync('./ssl/client-key.pem'),
cert: fs.readFileSync('./ssl/client-cert.pem')
}
}
});
性能优化技巧
1. 连接复用
javascript
// 不好的做法:每次都创建新连接
function badExample() {
const sequelize = new Sequelize(/* config */);
return sequelize.query('SELECT * FROM users');
}
// 好的做法:复用连接
const sequelize = new Sequelize(/* config */);
function goodExample() {
return sequelize.query('SELECT * FROM users');
}
2. 预热连接池
javascript
async function warmUpPool() {
const promises = [];
for (let i = 0; i < 5; i++) {
promises.push(sequelize.query('SELECT 1'));
}
await Promise.all(promises);
console.log('连接池预热完成');
}
// 应用启动时预热
warmUpPool();
3. 优雅关闭
javascript
async function gracefulShutdown() {
console.log('正在关闭数据库连接...');
await sequelize.close();
console.log('数据库连接已关闭');
process.exit(0);
}
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);
实战案例
我来分享一个实际项目中的连接配置:
javascript
// config/database.js
const { Sequelize } = require('sequelize');
class DatabaseManager {
constructor() {
this.sequelize = null;
this.isConnected = false;
}
async connect() {
if (this.isConnected) return this.sequelize;
this.sequelize = new Sequelize({
dialect: 'postgres',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
username: process.env.DB_USER,
password: process.env.DB_PASS,
pool: {
max: 15,
min: 3,
acquire: 60000,
idle: 10000,
handleDisconnects: true
},
logging: process.env.NODE_ENV === 'development' ? console.log : false,
retry: {
max: 3,
timeout: 5000
}
});
try {
await this.sequelize.authenticate();
this.isConnected = true;
console.log('✅ 数据库连接成功');
return this.sequelize;
} catch (error) {
console.error('❌ 数据库连接失败:', error);
throw error;
}
}
async disconnect() {
if (this.sequelize) {
await this.sequelize.close();
this.isConnected = false;
console.log('数据库连接已关闭');
}
}
}
module.exports = new DatabaseManager();
总结
今天我们深入学习了 Sequelize 的数据库连接,主要内容包括:
- ✅ 多种连接方式的使用场景
- ✅ 连接池的配置和优化
- ✅ 连接监控和健康检查
- ✅ 多数据库连接管理
- ✅ 常见问题的解决方案
- ✅ 性能优化技巧
掌握了这些知识,你就能够:
- 根据项目规模合理配置连接池
- 及时发现和解决连接问题
- 优化数据库连接性能
- 处理复杂的多数据库场景
下一篇文章,我们将开始学习 Sequelize 的核心功能 - 模型定义和基础操作。这是真正开始写业务代码的第一步,敬请期待!
相关文章推荐:
有问题欢迎留言讨论,我会及时回复大家!