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

NestJS+TypeORM多租户多数据库处理方案优化咨询

多租户数据库连接优化方案解析

当前方案的核心问题

你现在的实现每次请求都创建并初始化新的DataSource,这绝非最优解——数据库连接的建立、ORM元数据加载都是高开销操作,频繁执行会导致显著的性能损耗,高并发场景下甚至会引发服务雪崩。

最优优化方向:缓存已初始化的DataSource + 复用连接池

核心思路是为每个租户缓存对应的DataSource实例,避免重复创建,同时依托数据库自身的连接池复用物理连接,大幅降低资源消耗。

具体实现代码调整

  1. 在DatabaseService中添加租户DataSource缓存
    用Map存储已初始化的DataSource,以租户数据库名作为key:

    private tenantDataSources = new Map<string, DataSource>();
    
  2. 修改getDbConnForUser方法,优先复用缓存
    先检查缓存中是否存在对应租户的DataSource,存在则直接返回;不存在再创建初始化并加入缓存:

    async getDbConnForUser() {
        // 保留原有用户校验逻辑
        const user = this.utilityService.getUserFromRequestContext();
        if (!user) throw new UnauthorizedException('Invalid User');
    
        const subdomain = user.subdomain;
        const userSubdomains = await this.tenantService.findUserAndSubdomainsByEmail(user.email);
        if (userSubdomains.length === 0) throw new UnauthorizedException('Invalid User');
    
        if (!userSubdomains.find(ud => ud.subdomain === subdomain)) {
            throw new UnauthorizedException('Forbidden Access');
        }
    
        const tenant = await this.tenantService.getMasterTenantBySubdomain(subdomain);
        if (!tenant) throw new NotFoundException('Tenant not found');
    
        // 优先从缓存获取
        if (this.tenantDataSources.has(tenant.dbName)) {
            return this.tenantDataSources.get(tenant.dbName);
        }
    
        // 缓存不存在时创建新DataSource
        const newDataSource = new DataSource({
            type: 'postgres',
            host: this.configService.get('TENANT_DB_HOST'),
            port: this.configService.get('DB_PORT'),
            username: this.configService.get('TENANT_DB_USERNAME'),
            password: this.configService.get('TENANT_DB_PASSWORD'),
            database: tenant.dbName,
            entities: [join(process.cwd(), 'dist/**/tenant/*.entity.js')],
            subscribers: [join(process.cwd(), 'dist/**/subscribers/*.subscriber.js')],
            synchronize: false, // 生产环境必须关闭
            // 配置连接池参数,根据业务规模调整
            poolSize: 10,
            maxQueryExecutionTime: 5000
        });
    
        await newDataSource.initialize();
        this.tenantDataSources.set(tenant.dbName, newDataSource);
        return newDataSource;
    }
    
  3. 关键注意事项

    • 连接池配置:ORM的poolSize需与PostgreSQL的max_connections参数匹配,避免超出数据库连接上限。
    • 缓存清理:若存在租户删除场景,需从缓存中移除对应DataSource并调用destroy()释放资源。
    • 生产环境禁用synchronize:严格按照注释要求关闭,防止意外修改数据库结构。

长期架构可选方向

当租户规模持续增长时,单租户单库模式的运维成本会上升,可考虑以下替代方案:

  • Schema隔离:所有租户共用一个数据库,每个租户对应独立Schema,通过切换Schema实现隔离,无需维护多个DataSource。
  • 行级隔离:所有租户数据存储在同一张表,通过tenant_id字段区分,维护成本最低,但需严格控制权限并优化查询性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 13:36:00