跳到主要内容

Sequelize 数据库连接实战 - 连接池配置与最佳实践

· 阅读需 6 分钟
一介布衣
全栈开发者

发布时间:2024-01-15
作者:一介布衣
标签:Sequelize, 数据库连接, 连接池, 性能优化

前言

上一篇文章我们搭建好了 Sequelize 的开发环境,今天咱们来深入了解数据库连接的各种姿势。说实话,数据库连接这块看似简单,但里面的门道还真不少。

我记得刚开始用 Sequelize 的时候,经常遇到连接超时、连接池耗尽等问题,特别是在高并发场景下,这些问题更加明显。经过一段时间的摸索和踩坑,总结了一些实用的经验,今天分享给大家。

连接方式详解

Sequelize 提供了多种数据库连接方式,我们来逐一了解。

1. 连接字符串方式

这是最简单直接的方式:

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. 参数对象方式

这是我推荐的方式,配置更加灵活:

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. 分离配置方式

在实际项目中,我喜欢把配置单独抽出来:

// 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 性能优化的关键,我们来详细了解一下。

连接池参数详解

pool: {
max: 10, // 最大连接数
min: 0, // 最小连接数
acquire: 30000, // 获取连接的最大等待时间(毫秒)
idle: 10000, // 连接空闲多久后释放(毫秒)
evict: 1000, // 检查空闲连接的间隔时间
handleDisconnects: true // 自动处理断开的连接
}

不同场景的连接池配置

小型应用(日活 < 1000)

pool: {
max: 5,
min: 1,
acquire: 30000,
idle: 10000
}

中型应用(日活 1000-10000)

pool: {
max: 15,
min: 3,
acquire: 60000,
idle: 10000
}

大型应用(日活 > 10000)

pool: {
max: 30,
min: 10,
acquire: 60000,
idle: 5000
}

连接池监控

我写了一个简单的连接池监控函数:

function monitorConnectionPool(sequelize) {
const pool = sequelize.connectionManager.pool;

setInterval(() => {
console.log('连接池状态:', {
总连接数: pool.size,
可用连接: pool.available,
使用中连接: pool.using,
等待连接: pool.waiting
});
}, 10000); // 每10秒检查一次
}

// 使用
monitorConnectionPool(sequelize);

连接测试与健康检查

基础连接测试

async function testConnection() {
try {
await sequelize.authenticate();
console.log('✅ 数据库连接正常');
return true;
} catch (error) {
console.error('❌ 数据库连接失败:', error.message);
return false;
}
}

高级健康检查

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);

多数据库连接管理

在一些复杂的项目中,可能需要连接多个数据库:

// 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
};

使用时:

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

原因:连接池耗尽或数据库响应慢

解决方案

// 增加连接池大小和超时时间
pool: {
max: 20,
acquire: 60000
}

// 或者优化查询,减少长时间占用连接

2. 连接断开

现象SequelizeConnectionError: Connection lost

解决方案

// 启用自动重连
pool: {
handleDisconnects: true,
retry: {
max: 3
}
}

// 或者手动重连
sequelize.connectionManager.initPools();

3. SSL 连接问题

现象:SSL 相关错误

解决方案

// 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. 连接复用

// 不好的做法:每次都创建新连接
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. 预热连接池

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. 优雅关闭

async function gracefulShutdown() {
console.log('正在关闭数据库连接...');
await sequelize.close();
console.log('数据库连接已关闭');
process.exit(0);
}

process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);

实战案例

我来分享一个实际项目中的连接配置:

// 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 的核心功能 - 模型定义和基础操作。这是真正开始写业务代码的第一步,敬请期待!


相关文章推荐:

有问题欢迎留言讨论,我会及时回复大家!