多租户SaaS应用核心 Java+MySQL实现一套代码隔离多家企业数据

多租户SaaS数据隔离方案

独立数据库模式

为每个租户分配独立的MySQL数据库实例,通过动态数据源切换实现隔离。代码层面通过租户ID识别当前请求所属数据库。

// 动态数据源配置示例
public class TenantDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return TenantContext.getCurrentTenant();
    }
}

共享数据库分Schema

同一MySQL实例下为不同租户创建独立Schema,通过Hibernate或MyBatis拦截器动态修改SQL表名前缀。

-- 创建租户Schema示例
CREATE SCHEMA tenant_1;
CREATE TABLE tenant_1.users (id INT, name VARCHAR(50));

共享表租户ID隔离

所有表增加tenant_id字段,SQL查询自动附加租户过滤条件。需在DAO层统一处理。

// MyBatis拦截器示例
@Intercepts(@Signature(type= Executor.class, method="query",
        args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class TenantInterceptor implements Interceptor {
    public Object intercept(Invocation invocation) {
        BoundSql boundSql = ((MappedStatement)invocation.getArgs()[0]).getBoundSql();
        String newSql = boundSql.getSql() + " WHERE tenant_id = " + TenantContext.getCurrentTenant();
        resetSql(invocation, newSql);
        return invocation.proceed();
    }
}

行级安全策略

MySQL 8.0+可使用行级安全策略自动过滤数据,无需修改应用代码。

CREATE POLICY tenant_policy ON users 
    USING (tenant_id = CURRENT_TENANT_ID());

缓存隔离处理

Redis等缓存系统需为不同租户添加前缀,防止数据串扰。

// Redis缓存key处理示例
public String buildTenantKey(String rawKey) {
    return "tenant:" + TenantContext.getCurrentTenant() + ":" + rawKey;
}

租户上下文管理

通过过滤器或拦截器解析请求头/URL参数获取租户标识,存入ThreadLocal。

// 租户上下文示例
public class TenantContext {
    private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
    
    public static void setCurrentTenant(String tenant) {
        currentTenant.set(tenant);
    }
    
    public static String getCurrentTenant() {
        return currentTenant.get();
    }
}

分布式ID生成

采用雪花算法等分布式ID生成方案,确保跨租户ID唯一性。

// 雪花ID生成示例
public class SnowflakeIdGenerator {
    private final long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards");
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - epoch) << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | sequence;
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值