嵌套map模拟多租户隔离:外层键为租户id,内层键为业务id,配合threadlocal绑定租户上下文与租户级reentrantlock实现逻辑隔离,适用于教学、测试等轻量场景。

用嵌套 Map 模拟多租户数据隔离,核心是把“租户”作为第一层键,再把业务实体(如用户、订单)作为第二层键,配合线程级 IO 隔离(比如 ThreadLocal + 读写锁),就能在无数据库、无框架的极简场景下做到逻辑隔离。它不适用于生产系统,但对教学演示、原型验证、单元测试或本地沙箱非常实用。
嵌套 Map 结构设计:租户为根,实体为叶
采用 Map<string map object>></string> 结构:
- 外层 key 是租户 ID(如
"tenant-a"),代表独立的数据空间; - 内层 key 是具体记录 ID(如
"order-1001"),value 是该租户下的实际对象(如 Order 实例); - 不同租户的内层 Map 完全独立,互不可见、不可越界访问。
运行时租户上下文绑定:靠 ThreadLocal 切换视角
每次请求进来时,必须明确指定当前操作属于哪个租户,并将其绑定到当前线程:
- 定义
TenantContext类,内部用ThreadLocal<string></string>存租户 ID; - 在拦截器或入口处调用
TenantContext.set("tenant-b"); - 所有数据操作方法(如
getOrder(id))先从TenantContext.get()拿 ID,再查外层 Map; - 请求结束前务必调用
TenantContext.clear(),防止线程复用导致上下文污染。
IO 隔离:读写分离 + 租户粒度锁
避免多线程并发修改同一租户数据引发错乱,不依赖全局锁,而是按租户加细粒度锁:
- 维护一个
ConcurrentHashMap<string reentrantlock></string>,key 是租户 ID,value 是专属锁; - 执行写操作(如
saveOrder(order))前,先获取该租户对应的锁; - 读操作可不加锁,但若要求强一致性(如统计总数),也应短暂持锁或使用
Collections.unmodifiableMap返回快照; - 锁只作用于本租户,
tenant-a的写不影响tenant-b的并发读写。
安全增强:禁止跨租户访问的硬约束
光靠约定不够,需代码级防御:
- 所有公共操作方法(如
deleteOrder(String orderId))不接受租户参数,只从TenantContext获取; - 在数据访问层加校验:若传入的
orderId不在当前租户的内层 Map 中,直接抛异常,不沉默失败; - 禁止暴露外层 Map 引用,所有访问必须通过封装好的 Service 方法,例如:
public Order getOrder(String id) {
String tenant = TenantContext.get();
MaptenantMap = data.get(tenant);
if (tenantMap == null || !tenantMap.containsKey(id)) {
throw new TenantAccessException("Order not found in tenant: " + tenant);
}
return tenantMap.get(id);
}











