实现多租户的话一般有以下几种实现方案如下所示方案描述优点缺点使用场景独立数据库每个租户一个独立的DB实例隔离性最好物理隔离单租户故障不影响其他人备份回复容易成本最高连接池管理迁移脚本执行运维复杂资源利用率低对数据敏感的大客户独立schema同一个DB实例每个租户一个Schema隔离性较好逻辑上分开运维成本适中跨租户统计困难数据库连接数有上限MySQL下Schema过多会影响性能中型SaaS对隔离有一定要求且租户数量可控共享表租户id所有租户共用一套表通过tenant_id 字段区分成本最低资源利用率最高开发维护简单跨租户查询容易隔离性最弱代码层面一旦漏掉租户id容易造成数据泄露绝大多数saas平台小微租户多对成本敏感我的决策我会优选采用方案三共享表租户ID因为性价比最高。技术落地一、租户上下文的传递在网关或者拦截器中从Header中解析出tarentId并使用ThreadLocal将TarentId放入当前线程的上下文中二、数据隔离的核心利用Mybatis的拦截器TenantInterceptor实现他的getTenantId方法多线程情况下上下文丢失导致找不到tenantId;可以利用spring的TaskDecorator它允许我们在任务执行前后执行制定逻辑我们可以通过配置在任务执行前 设置tarentId在任务执行后清理importorg.springframework.context.annotation.Bean;importorg.springframework.core.task.TaskDecorator;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;public class ExecutorConfig{// 假设这是你用于存储上下文的 ThreadLocal private static final ThreadLocalStringMY_CONTEXTnew ThreadLocal();Bean public ThreadPoolTaskExecutortaskExecutor(){ThreadPoolTaskExecutor executornew ThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(100);// 关键配置设置 TaskDecorator executor.setTaskDecorator(newTaskDecorator(){Override public Runnable decorate(Runnable runnable){//1. 在主线程中捕获上下文数据 String contextValueMY_CONTEXT.get();//2. 返回一个包装后的 Runnablereturn()-{try{//3. 在子线程中设置捕获到的上下文if(contextValue!null){MY_CONTEXT.set(contextValue);}//4. 执行原始任务 runnable.run();}finally{//5. 任务执行完毕清理上下文防止内存泄漏和数据污染 MY_CONTEXT.remove();}};}});executor.initialize();returnexecutor;}}