In a multi-tenant architecture, data isolation is a core technical requirement for protecting tenant data security and safety. An effective isolation mechanism requires a defense-in-depth system encompassing database design, application logic, storage architecture, and access control, ensuring complete data isolation between tenants while maintaining system maintainability and scalability.
Three mainstream isolation solutions are available at the database level, each suitable for different business scenarios. Independent database instances allocate a dedicated database to each tenant, achieving complete physical isolation. This solution offers the highest security, eliminating the risk of data leakage between tenants and is suitable for scenarios such as finance and healthcare where data isolation is crucial. However, this comes at the cost of higher hardware costs and maintenance complexity that scales linearly with the number of tenants. In terms of code implementation, dynamic data source routing enables connection management between different tenant databases:
java
public class TenantDataSourceRouter {
private Map<String, DataSource> tenantDataSources;
public DataSource getDataSource(String tenantId) {
return tenantDataSources.get(tenantId);
}
}
The shared database, independent schema mode creates independent schemas for each tenant within the same database instance. This solution strikes a balance between isolation and resource utilization. Tenants have independent table structures but share database services. It uses fewer resources than separate instances while ensuring logical data isolation. Managing multiple schemas requires a standardized change management process, and all schema changes must be applied synchronously to all tenant schemas. SQL queries must explicitly specify the schema:
sql
SELECT FROM tenant_001.orders WHERE user_id = 100;
The shared database, shared table structure model stores all tenant data in the same set of tables within the same database, distinguishing tenants using the tenant_id field. This solution offers the highest resource utilization and scalability, but also the highest implementation complexity. Every database query must include a tenant_id filter, and any inadvertent error could lead to data leakage. To ensure query security, the tenant context should be mandatory injected into the database access layer:
java
@Repository
public class OrderRepository {
@Autowired
private TenantContext tenantContext;
public List<Order> findByUser(Long userId) {
String sql = "SELECT FROM orders WHERE tenant_id = ? AND user_id = ?";
return jdbcTemplate.query(sql, tenantContext.getId(), userId);
}
}
As the core carrier of business logic, the application layer must establish a comprehensive tenant context management system. Each API request should carry a tenant identifier. Typically, after the user authenticates, their tenant information is stored in a thread-local variable:
java
public class TenantContext {
private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();
public static void setTenantId(String tenantId) {
currentTenant.set(tenantId);
}
public static String getTenantId() {
return currentTenant.get();
}
}
The data access layer needs to ensure that all database operations are automatically associated with the current tenant. When using an ORM framework such as MyBatis, you can automatically inject the tenant_id condition through a custom interceptor:
java
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class TenantInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object parameter = invocation.getArgs()[1];
if(parameter instanceof Map) {
((Map) parameter).put("tenantId", TenantContext.getTenantId());
}
return invocation.proceed();
}
}
Sensitive data should be encrypted at the storage layer, and the encryption strategy should be selected according to different isolation requirements. Application-level encryption uses a unified key to encrypt all tenant data. It is simple to implement but relatively less secure. Tenant-level encryption assigns each tenant a separate encryption key, achieving data isolation at the encryption level. Even if data is illegally accessed, it cannot be decrypted. The key management system must securely store the master key and ensure the secure distribution of subkeys:
python
class TenantEncryptionService:
def __init__(self, key_management_client):
self.key_management_client = key_management_client
def encrypt_data(self, tenant_id, plaintext):
tenant_key = self.key_management_client.get_tenant_key(tenant_id)
cipher = AES.new(tenant_key, AES.MODE_GCM)
ciphertext, tag = cipher.encrypt_and_digest(plaintext.encode())
return cipher.nonce + ciphertext + tag
Physical storage isolation can employ different strategies depending on the sensitivity of the data. Logical isolation can be used for general business data, distinguishing tenants within the storage system by path or prefix. Highly sensitive data should be physically isolated, with separate storage clusters or cloud storage buckets allocated to tenants with different security levels.
Network isolation prevents unauthorized access between tenants. Virtual private cloud technology creates a logically isolated network environment for each tenant, and security group rules are used to restrict access sources. For multi-tenant applications in a cloud environment, virtual network peering or transit gateways can be used to achieve necessary network connectivity while maintaining isolation boundaries.
System-level resource isolation ensures fair distribution of computing resources. Containerized deployments allocate independent container instances to each tenant, and resource usage is restricted using cgroups. Kubernetes namespaces create virtual clusters for each tenant and implement resource quota management:
yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: tenant-quota
namespace: tenant-001
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
limits.cpu: "4"
limits.memory: 8Gi
Complete data access auditing is key to detecting isolation failures. Record the tenant context, access time, and source IP address of all data access operations, and regularly analyze abnormal access patterns. Establish a data lineage tracking system to mark the tenant ownership of data throughout the entire data processing chain:
sql
CREATE TABLE data_access_audit (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
tenant_id VARCHAR(50) NOT NULL,
user_id VARCHAR(100) NOT NULL,
operation_type VARCHAR(20) NOT NULL,
table_name VARCHAR(100) NOT NULL,
access_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
source_ip VARCHAR(45)
);
The real-time monitoring system should detect possible data leakage risks, such as abnormally large query result sets and sudden changes in access patterns. Set alarm rules to immediately notify the security team when potential unauthorized data access is detected.
Multi-tenant data isolation is a systematic project, and the appropriate isolation level and implementation plan need to be selected according to the business scenario. During the design and implementation process, security requirements, system performance, and development and operation costs should be balanced to establish a complete isolation system from applications to underlying infrastructure.