如何用Node.js+Sequelize实现多客户动态MySQL数据库连接
实现Sequelize动态切换客户数据库连接的方案
核心思路是创建连接缓存+动态生成数据库实例,避免重复初始化连接,同时根据当前客户标识匹配对应的数据库schema。以下是具体实现方案:
1. 重构数据库配置与连接逻辑
替换原来固定的Sequelize实例初始化代码,改成动态生成的方式,同时加入缓存复用已创建的连接:
const Sequelize = require('sequelize'); const Op = Sequelize.Op; const fs = require('fs'); const path = require('path'); // 通用数据库配置模板,仅schema名称动态替换 const baseDbConfig = { dialect: 'mysql', host: '127.0.0.1', port: '3306', dialectOptions: { supportBigNumbers: true, bigNumberStrings: true }, pool: { max: 5, min: 0, acquire: 30000, idle: 10000 }, logging: console.log }; // 缓存已初始化的数据库连接,避免重复创建 const connectionCache = {}; // 为指定Sequelize实例加载模型并处理关联 function loadCustomerModels(sequelize) { const db = { Op }; // 加载所有模型文件 fs.readdirSync(__dirname) .filter(file => (file.indexOf('.') !== 0) && (file !== 'index.js')) .forEach(file => { const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes); db[model.name] = model; }); // 执行模型关联配置 Object.keys(db).forEach(modelName => { if ('associate' in db[modelName]) { db[modelName].associate(db); } }); db.sequelize = sequelize; db.Sequelize = Sequelize; return db; } // 根据客户ID获取对应的数据库实例 function getDbForCustomer(customerId) { // 替换成你的客户schema命名规则,比如customer_1对应db_schema_customer_1 const targetSchema = `db_schema_${customerId}`; // 缓存存在则直接返回 if (connectionCache[targetSchema]) { return connectionCache[targetSchema]; } // 创建新的Sequelize连接实例 const sequelize = new Sequelize(targetSchema, 'user', 'password', baseDbConfig); // 验证连接(可选,也可延迟到首次查询时自动验证) sequelize.authenticate() .then(() => console.log(`Connected to schema: ${targetSchema}`)) .catch(err => { console.error(`Failed to connect to ${targetSchema}:`, err); throw err; // 连接失败时抛出,避免缓存无效连接 }); // 加载该连接对应的模型 const customerDb = loadCustomerModels(sequelize); // 存入缓存 connectionCache[targetSchema] = customerDb; return customerDb; } module.exports = { getDbForCustomer };
2. 业务代码中动态调用
在接口或业务逻辑里,先获取当前客户的标识(比如从请求头、域名、用户登录信息中提取),再调用getDbForCustomer获取对应数据库实例:
const { getDbForCustomer } = require('./db'); // 以Express接口为例 app.get('/api/orders', (req, res) => { // 从请求头获取客户ID,实际可根据业务场景调整获取方式 const customerId = req.headers['x-customer-id']; if (!customerId) { return res.status(400).send('Missing customer ID'); } try { const db = getDbForCustomer(customerId); // 使用该数据库实例操作数据 db.Order.findAll({ where: { status: 'completed' } }) .then(orders => res.json(orders)) .catch(err => res.status(500).send(err.message)); } catch (err) { res.status(500).send(`Database connection error: ${err.message}`); } });
关键注意事项
- 客户标识获取:可根据业务场景选择不同方式,比如子域名解析(
customer1.yourapp.com提取customer1)、JWT令牌中的客户ID、请求参数等 - 缓存管理:如果客户数量极大,需考虑添加缓存清理机制,比如定期移除长时间未使用的连接实例,避免内存泄漏
- schema一致性:确保所有客户的数据库表结构完全一致,因为模型是共用的;如果存在差异化需求,需扩展模型加载逻辑(比如按客户分组加载不同模型)
- 连接池配置:每个客户的连接池是独立的,需根据服务器性能调整
pool.max值,避免数据库连接数过载
内容的提问来源于stack exchange,提问作者luis miguel castro martinez
相关产品推荐
相关产品推荐

