You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NestJs与Sequelize中管理多数据库连接(复用、关闭)?

NestJS + Sequelize 实现动态多租户数据库连接(连接复用+请求路由)

核心方案概述

通过JWT解析获取用户所属公司ID生成对应数据库名称;用连接池缓存复用已创建的Sequelize实例,避免重复初始化;通过NestJS请求拦截器将当前请求对应的连接绑定到请求上下文,确保每个请求路由到正确的数据库;同时实现连接的自动回收与闲置关闭。


实现步骤

1. 连接池管理工具:缓存已创建的Sequelize实例

创建单例工具类,统一管理所有租户的数据库连接,避免重复创建:

import { Sequelize } from 'sequelize-typescript';
import config from './config'; // 你的配置文件

// 缓存已创建的Sequelize实例
const connectionCache = new Map<string, Sequelize>();

export class TenantConnectionManager {
  // 获取或创建租户连接
  static async getConnection(dbname: string): Promise<Sequelize> {
    const dbKey = `company_${dbname}`;
    // 缓存存在则直接返回
    if (connectionCache.has(dbKey)) {
      return connectionCache.get(dbKey)!;
    }

    // 缓存不存在则创建新连接
    const sequelize = new Sequelize(dbKey, config().database.user, config().database.pass, {
      dialect: 'mysql',
      port: config().database.port,
      replication: {
        write: {
          host: config().database.host,
          port: config().database.port,
        },
        read: [{
          host: config().database.hostRead,
          port: config().database.port,
        }]
      },
      pool: {
        max: 50,
        idle: 30000, // 闲置30秒自动释放连接池内的连接
        acquire: 60000, // 获取连接超时时间
      },
      define: {
        timestamps: false,
      },
    });

    // 注册模型
    sequelize.addModels([/* 你的实体类集合 */]);
    // 验证连接(生产环境建议关闭自动sync,改用手动迁移)
    await sequelize.authenticate();
    // await sequelize.sync(); 

    // 加入缓存
    connectionCache.set(dbKey, sequelize);

    // 闲置超过1小时自动回收整个连接实例
    sequelize.on('idle', () => {
      setTimeout(() => {
        if (sequelize.connectionManager.pool.count === 0) {
          sequelize.close();
          connectionCache.delete(dbKey);
        }
      }, 3600000);
    });

    return sequelize;
  }

  // 手动关闭指定租户连接
  static async closeConnection(dbname: string): Promise<void> {
    const dbKey = `company_${dbname}`;
    const sequelize = connectionCache.get(dbKey);
    if (sequelize) {
      await sequelize.close();
      connectionCache.delete(dbKey);
    }
  }
}

2. 请求拦截器:绑定租户连接到请求上下文

编写拦截器,从JWT解析公司ID,获取对应连接并绑定到请求对象:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { TenantConnectionManager } from './tenant-connection-manager';

@Injectable()
export class TenantConnectionInterceptor implements NestInterceptor {
  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
    const request = context.switchToHttp().getRequest();
    
    // 从JWT解析后的用户信息中获取公司ID(假设已通过AuthGuard解析到request.user)
    const companyId = request.user.companyId;
    if (!companyId) {
      throw new Error('请求中缺少公司ID');
    }

    // 获取租户连接并绑定到请求上下文
    const sequelize = await TenantConnectionManager.getConnection(companyId);
    request.tenantConnection = sequelize;

    return next.handle();
  }
}

3. 动态模型提供者:注入当前请求的连接

自定义模型提供者,让服务自动获取当前请求对应的租户连接:

import { Provider } from '@nestjs/common';
import { getModelToken } from '@nestjs/sequelize';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';

// 生成动态模型提供者的工具函数
export function createTenantModelProvider(model: any): Provider {
  return {
    provide: getModelToken(model),
    useFactory: (context: ExecutionContextHost) => {
      const request = context.switchToHttp().getRequest();
      // 从请求上下文获取租户连接,返回对应模型实例
      const sequelize = request.tenantConnection;
      return sequelize.getRepository(model);
    },
    inject: [ExecutionContextHost],
  };
}

在业务模块中注册提供者:

import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { TenantConnectionInterceptor } from './tenant-connection.interceptor';
import { User } from './entities/user.entity';
import { createTenantModelProvider } from './tenant-model.provider';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: TenantConnectionInterceptor,
    },
    createTenantModelProvider(User),
    UserService,
  ],
  controllers: [UserController],
})
export class UserModule {}

4. 服务中使用动态模型

直接注入模型即可自动使用当前租户的数据库连接:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { User } from './entities/user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectModel(User) private readonly userModel: typeof User,
  ) {}

  async findAll(): Promise<User[]> {
    // 自动使用当前租户的数据库连接查询
    return this.userModel.findAll();
  }
}

关键问题解决

  1. 连接复用:通过connectionCache缓存已创建的Sequelize实例,相同租户的请求直接复用,避免重复初始化开销。
  2. 请求路由:拦截器将租户连接绑定到请求上下文,确保每个请求使用对应租户的数据库。
  3. 连接回收:利用Sequelize连接池的idle事件,结合超时逻辑自动回收闲置过久的连接;同时提供手动关闭接口,适配租户注销等场景。

内容的提问来源于stack exchange,提问作者ssSilas_s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 11:05:06