Giao diện
Multi-tenant
Hệ thống SaaS phục vụ nhiều khách hàng (tenant) trên cùng một hạ tầng. Câu hỏi chính: dữ liệu các tenant nằm chung tới mức nào.
Ba mô hình
| Mô hình | Cách làm | Ưu điểm | Nhược điểm |
|---|---|---|---|
| Chung bảng | Cột tenant_id ở mọi bảng | Rẻ nhất, dễ vận hành, migration một lần | Một tenant lớn ảnh hưởng tất cả; quên lọc tenant_id là lộ dữ liệu |
| Schema riêng (PostgreSQL) | Mỗi tenant một schema trong cùng database | Tách bạch hơn, backup/khôi phục từng tenant | Migration nhân theo số tenant; hàng nghìn schema khó quản lý |
| Database riêng | Mỗi tenant một database hoặc cụm | Cô lập hoàn toàn, tuỳ chỉnh và đặt vùng theo tenant | Đắt, vận hành nhân N |
Thực tế hay dùng kết hợp: đa số tenant dùng chung bảng, khách rất lớn hoặc có yêu cầu pháp lý được tách database riêng.
Chung bảng: làm cho đúng
tenant_idở mọi bảng, kể cả bảng con, để mọi query lọc được mà không phải join.tenant_idđứng đầu khoá chính và mọi index, ví dụPRIMARY KEY (tenant_id, id). Dữ liệu một tenant nằm gần nhau trong B+Tree, mọi query theo tenant seek gọn, và sau này shard theo tenant dễ dàng (MySQL chương 10).- UNIQUE theo nghiệp vụ cũng phải có
tenant_id:UNIQUE (tenant_id, email), vì hai tenant khác nhau có thể có cùng email. - Chặn quên lọc ở tầng thấp nhất có thể:
- PostgreSQL có Row-Level Security:
CREATE POLICY … USING (tenant_id = current_setting('app.tenant_id')::bigint), database tự lọc. - MySQL không có RLS: lọc trong repository/ORM (global scope), kèm test tự động kiểm tra mọi query có điều kiện tenant.
- PostgreSQL có Row-Level Security:
- Giới hạn tài nguyên theo tenant (rate limit, quota) để một tenant không làm chậm tất cả.
Ghi nhớ
- Chung bảng là mặc định; tách database cho khách lớn hoặc có yêu cầu riêng.
tenant_idở mọi bảng, đứng đầu khoá chính và index, có trong UNIQUE.- Chặn quên lọc bằng RLS (PostgreSQL) hoặc lớp truy cập dữ liệu + test (MySQL).