14 thg 10, 2025·8 phút đọc

Các lựa chọn mô hình dữ liệu SaaS đa-tenant cho backend không cần code

Các lựa chọn mô hình dữ liệu SaaS đa-tenant ảnh hưởng tới bảo mật, báo cáo và hiệu năng. So sánh tenant_id, schema riêng và database riêng cho từng khách hàng cùng các đánh đổi rõ ràng.

Các lựa chọn mô hình dữ liệu SaaS đa-tenant cho backend không cần code

Vấn đề: giữ các tenant tách biệt mà không làm chậm hệ thống

Multi-tenancy nghĩa là một sản phẩm phần mềm phục vụ nhiều khách hàng (tenant), và mỗi tenant chỉ được nhìn thấy dữ liệu của họ. Khó khăn là làm sao đảm bảo điều đó nhất quán: không chỉ trên một màn hình, mà trên mọi gọi API, bảng điều khiển admin, xuất dữ liệu và job nền.

Mô hình dữ liệu ảnh hưởng đến hoạt động hàng ngày nhiều hơn hầu hết đội ngũ tưởng tượng. Nó định hình quyền truy cập, báo cáo, tốc độ truy vấn khi bạn lớn lên, và độ rủi ro của một bug “nhỏ”. Thiếu một bộ lọc là bạn có thể lộ dữ liệu. Cô lập quá mức thì báo cáo trở nên phiền toái.

Có ba cách phổ biến để cấu trúc mô hình dữ liệu SaaS đa-tenant:

  • Một cơ sở dữ liệu nơi mọi bảng đều có tenant_id
  • Schema riêng cho từng tenant trong cùng một database
  • Cơ sở dữ liệu riêng cho từng tenant

Ngay cả khi bạn xây giao diện bằng backend không cần code, các đánh đổi tương tự vẫn tồn tại. Các công cụ như AppMaster sinh ra code backend thực và cấu trúc database từ thiết kế của bạn, nên quyết định mô hình ban đầu sẽ hiện ngay trong hành vi và hiệu năng production.

Hãy tưởng tượng một công cụ helpdesk. Nếu mỗi dòng ticket có tenant_id, thì dễ truy vấn “tất cả ticket mở”, nhưng bạn phải đảm bảo kiểm tra tenant khắp mọi nơi. Nếu mỗi tenant có schema hoặc database riêng, tách biệt mặc định tốt hơn, nhưng báo cáo xuyên-tenant (ví dụ “thời gian trung bình đóng ticket cho mọi khách hàng”) sẽ phức tạp hơn.

Mục tiêu là tách biệt bạn có thể tin tưởng mà không làm khó báo cáo, hỗ trợ và tăng trưởng.

Cách chọn nhanh: 4 câu hỏi giúp thu hẹp

Đừng bắt đầu bằng lý thuyết database. Bắt đầu bằng cách sử dụng sản phẩm và điều bạn cần vận hành hàng tuần.

Bốn câu hỏi thường làm rõ phương án

  1. Dữ liệu nhạy cảm đến mức nào, và bạn có bị quy định nghiêm ngặt không? Y tế, tài chính và hợp đồng khách hàng chặt chẽ thường đẩy bạn về phía tách biệt mạnh hơn (schema riêng hoặc database riêng). Nó giảm rủi ro và giúp kiểm toán dễ hơn.

  2. Bạn có cần báo cáo xuyên-tenant thường xuyên không? Nếu bạn thường cần metric “tất cả khách hàng” (sử dụng, doanh thu, hiệu năng), một database đơn với tenant_id thường là đơn giản nhất. Database riêng làm việc này khó hơn vì phải truy vấn nhiều nơi rồi gộp kết quả.

  3. Các tenant sẽ khác nhau đến đâu? Nếu tenant cần trường tuỳ biến, workflow riêng, hoặc tích hợp độc đáo, schema hoặc database riêng giảm khả năng thay đổi bị lan sang người khác. Nếu hầu hết tenant cùng cấu trúc, tenant_id vẫn gọn.

  4. Đội ngũ của bạn có thể vận hành thực tế được gì? Càng tách biệt nhiều thường đồng nghĩa việc quản lý nhiều hơn: nhiều backup, nhiều migration, nhiều thành phần và nhiều nơi có thể lỗi.

Cách tiếp cận thực tế là prototype hai lựa chọn hàng đầu, rồi thử các điểm đau thật: quy tắc quyền, truy vấn báo cáo, và cách rollout thay đổi khi mô hình phát triển.

Cách 1: một database với tenant_id trên mọi dòng

Đây là cấu hình phổ biến nhất: tất cả khách hàng dùng chung bảng, và mọi bản ghi thuộc tenant mang tenant_id. Vận hành đơn giản vì bạn chạy một database và một bộ migration.

Quy tắc là nghiêm ngặt: nếu một dòng thuộc tenant thì phải có tenant_id, và mọi truy vấn phải lọc theo nó. Các bảng thuộc tenant thường gồm users, roles, projects, tickets, invoices, messages, metadata file, và các bảng phụ kết nối dữ liệu tenant.

Để giảm rủi ro rò rỉ, coi tenant_id là không thương lượng:

  • Đặt tenant_id là bắt buộc (NOT NULL) trên các bảng thuộc tenant
  • Thêm index bắt đầu bằng tenant_id (ví dụ tenant_id, created_at)
  • Các quy tắc unique bao gồm tenant_id (như email unique theo tenant)
  • Truyền tenant_id qua mọi API và luồng nghiệp vụ, không chỉ form UI
  • Áp dụng ở backend, không chỉ bộ lọc phía client

Trong PostgreSQL, chính sách row-level security có thể là mạng lưới an toàn mạnh, đặc biệt khi truy vấn được sinh động.

Dữ liệu tham chiếu thường nằm trong hai nhóm: bảng chia sẻ (như countries) không có tenant_id, và catalogue theo tenant (như tag tuỳ chỉnh hoặc pipeline) có tenant_id.

Nếu bạn xây bằng AppMaster, một thói quen đơn giản ngăn phần lớn sự cố: gán tenant_id từ tenant của người dùng đã xác thực trước mọi thao tác tạo hoặc đọc trong Business Process, và giữ pattern đó nhất quán.

Tác động tới quyền: điều gì thay đổi với từng cách

Quyền là nơi multi-tenancy thành công hay thất bại. Bố cục dữ liệu bạn chọn thay đổi cách lưu người dùng, cách đóng khung truy vấn, và cách tránh những khoảnh khắc “úp” trong màn hình admin.

Với database đơn và tenant_id trên mọi dòng, các đội thường dùng một bảng Users chung và kết nối mỗi user với một tenant và một hay nhiều role. Quy tắc lớn vẫn là: mọi đọc và ghi phải có phạm vi tenant, kể cả các bảng “nhỏ” như settings, tags hay logs.

Với schema riêng, bạn thường giữ lớp định danh (login, password, MFA) chung ở một nơi, trong khi dữ liệu tenant nằm trong schema cho từng tenant. Quyền phần nào trở thành bài toán routing: app phải trỏ truy vấn tới schema đúng trước khi logic nghiệp vụ chạy.

Với database riêng, tách biệt mạnh nhất, nhưng logic quyền chuyển sang hạ tầng: chọn kết nối database đúng, quản lý credential, và xử lý tài khoản staff “toàn cục”.

Trên cả ba cách, một vài pattern luôn giảm rủi ro cross-tenant:

  • Đặt tenant_id vào session hoặc token auth và coi đó là bắt buộc.
  • Tập trung kiểm tra tenant ở một chỗ (middleware hoặc Business Process chung), không rải rác khắp endpoint.
  • Trong công cụ admin, hiển thị rõ ngữ cảnh tenant và yêu cầu chuyển tenant rõ ràng.
  • Với quyền support, dùng impersonation kèm audit log.

Trong AppMaster, điều này thường nghĩa là lưu ngữ cảnh tenant ngay sau khi xác thực và tái sử dụng nó ở endpoints và Business Processes để mọi truy vấn luôn có scope. Một nhân viên hỗ trợ chỉ được thấy đơn của tenant sau khi app đã đặt ngữ cảnh tenant, chứ không phải vì UI vô tình lọc đúng.

Báo cáo và hiệu năng với mô hình tenant_id

Create Tenant Safe Portals
Xây công cụ nội bộ và portal khách hàng giữ an toàn theo tenant khi bạn mở rộng.
Thử AppMaster

Với cách dùng một database và tenant_id, báo cáo thường đơn giản. Dashboard toàn cục (MRR, signups, usage) có thể chạy một truy vấn trên toàn bộ khách hàng, và báo cáo theo tenant là cùng truy vấn với bộ lọc.

Đổi chác là hiệu năng khi lớn. Khi bảng tăng kích thước, một tenant bận rộn có thể trở thành “hàng xóm ồn ào” bằng cách tạo nhiều dòng, kích hoạt nhiều ghi, và làm chậm các truy vấn chung nếu database phải quét quá nhiều dữ liệu.

Index giữ cho mô hình này khỏe mạnh. Hầu hết đọc theo tenant nên dùng index bắt đầu bằng tenant_id, để database nhảy ngay tới lát dữ liệu của tenant đó.

Một số gợi ý cơ bản:

  • Thêm index phức hợp với tenant_id là cột đầu (ví dụ tenant_id + created_at, tenant_id + status, tenant_id + user_id)
  • Giữ index thực sự toàn cục chỉ khi bạn cần truy vấn xuyên-tenant
  • Chú ý các join và filter quên tenant_id, vì chúng có thể gây quét chậm

Retention và xóa cũng cần kế hoạch vì lịch sử của một tenant có thể làm phình bảng cho mọi người. Nếu tenant có chính sách retention khác nhau, cân nhắc soft delete cộng job lưu trữ theo tenant, hoặc chuyển dòng cũ vào bảng archive có khoá tenant_id.

Cách 2: schema riêng cho từng tenant

Với schema riêng, bạn vẫn dùng một database PostgreSQL, nhưng mỗi tenant có schema riêng (ví dụ tenant_42). Bảng trong schema đó chỉ thuộc tenant đó. Nó giống như cho mỗi khách hàng một “mini database”, nhưng không tốn chi phí chạy nhiều database.

Cấu hình phổ biến là giữ dịch vụ toàn cục trong schema chia sẻ và dữ liệu tenant trong schema riêng. Sự phân chia thường dựa trên cái gì phải chia sẻ giữa mọi khách hàng và cái gì không bao giờ được trộn lẫn.

Phân chia điển hình:

  • Schema chia sẻ: bảng tenants, plans, hồ sơ tính phí, feature flags, cài đặt audit
  • Schema tenant: bảng nghiệp vụ như orders, tickets, inventory, projects, custom fields
  • Một số thành phần (tuỳ sản phẩm): users và roles, đặc biệt nếu người dùng có thể truy cập nhiều tenant

Mô hình này giảm rủi ro join chéo tenant vì bảng nằm ở namespace khác nhau. Nó cũng dễ backup/restore từng tenant hơn vì bạn có thể nhắm vào một schema.

Điều làm nhiều đội ngạc nhiên là migrations. Khi thêm bảng hoặc cột mới, bạn phải áp dụng thay đổi cho mọi schema tenant. Với 10 tenant thì dễ; với 1.000 tenant bạn cần quy trình: theo dõi version schema, chạy migration theo lô, và fail an toàn để một tenant hỏng không chặn cả hệ thống.

Dịch vụ chung như auth và billing thường ở ngoài schema tenant. Pattern hữu dụng là auth chia sẻ (một bảng user với bảng membership tenant) và billing chia sẻ (Stripe customer IDs, invoice), trong khi schema tenant lưu dữ liệu nghiệp vụ thuộc tenant.

Nếu dùng AppMaster, hãy hoạch định sớm cách Data Designer ánh xạ sang shared vs tenant schemas, và giữ dịch vụ toàn cục ổn định để schema tenant có thể phát triển mà không phá login hay thanh toán.

Báo cáo và hiệu năng với schema riêng

Plan Reporting From Day One
Thiết lập đường dẫn báo cáo cho dashboard tenant và metric admin toàn cục mà không cần giải pháp lộn xộn.
Bắt đầu

Schema riêng mặc định tách biệt mạnh hơn so với chỉ dùng tenant_id vì bảng vật lý khác nhau và quyền có thể đặt theo schema.

Báo cáo rất tốt khi hầu hết báo cáo là theo tenant. Truy vấn đơn giản vì đọc trực tiếp bảng của tenant mà không cần lọc liên tục. Mô hình này cũng hỗ trợ tenant “đặc biệt” cần thêm bảng hoặc cột tuỳ biến mà không bắt tất cả phải mang theo.

Báo cáo tổng hợp xuyên-tenant là điểm schema bắt đầu gây khó. Bạn cần lớp báo cáo có thể quét nhiều schema, hoặc duy trì các bảng tóm tắt chung trong schema chia sẻ.

Các pattern phổ biến:

  • Dashboard theo tenant chỉ truy vấn schema của tenant
  • Schema phân tích trung tâm với rollup hàng đêm từ từng tenant
  • Job xuất dữ liệu chuyển sang định dạng phù hợp cho data warehouse

Hiệu năng thường ổn cho workload theo tenant. Index nhỏ hơn theo tenant, và ghi nặng ở một schema ít ảnh hưởng tới các schema khác. Đổi lại là chi phí vận hành: provisioning tenant mới nghĩa là tạo schema, chạy migration, và giữ các schema đồng bộ khi mô hình thay đổi.

Schema phù hợp khi bạn muốn tách bạch nghiêm ngặt hơn mà không chạy nhiều database, hoặc khi bạn dự kiến tuỳ biến theo tenant.

Cách 3: cơ sở dữ liệu riêng cho từng tenant

Với cơ sở dữ liệu riêng, mỗi khách hàng có database riêng (hoặc database riêng trên cùng server). Đây là lựa chọn tách biệt nhất: nếu dữ liệu một tenant bị hỏng hoặc bị cấu hình sai, ít khả năng lan sang người khác.

Phù hợp cho môi trường bị quy định chặt (y tế, tài chính, chính phủ) hoặc khách hàng doanh nghiệp mong đợi tách biệt khắt khe, retention riêng, hoặc hiệu năng riêng.

Onboarding thành quy trình provisioning. Khi tenant mới đăng ký, hệ thống cần tạo hoặc clone database, áp dụng schema cơ bản (bảng, index, ràng buộc), tạo và lưu credential an toàn, và điều hướng API tới database đúng.

Nếu xây bằng AppMaster, quyết định quan trọng là lưu thư mục tenant ở đâu (bản đồ tenant -> connection database) và đảm bảo mọi request dùng đúng kết nối.

Upgrades và migrations là đánh đổi chính. Thay đổi schema giờ không còn là “chạy một lần”, mà là “chạy cho mọi tenant”. Điều này làm tăng công việc vận hành và rủi ro, nên các đội thường version schema và chạy migration như job kiểm soát theo tenant.

Ưu điểm là kiểm soát. Bạn có thể migrate tenant lớn trước, quan sát hiệu năng rồi dần dần roll out cho các tenant khác.

Báo cáo và hiệu năng với database riêng

Validate Your Best Two Options
So sánh tenant_id, schema riêng, và cơ sở dữ liệu riêng với cùng logic ứng dụng.
Thử ngay

Database riêng dễ suy nghĩ hơn. Truy vấn nhầm tenant ít xảy ra, và lỗi quyền thường ảnh hưởng mỗi tenant một cách độc lập.

Hiệu năng là điểm mạnh. Truy vấn nặng, import lớn, hoặc một báo cáo runaway ở Tenant A sẽ ít làm chậm Tenant B. Đây là bảo vệ tốt khỏi hàng xóm ồn ào và cho phép tinh chỉnh tài nguyên theo tenant.

Đổi lại, báo cáo toàn cục là phức tạp nhất vì dữ liệu tách rời. Các pattern thực tế gồm sao chép event quan trọng hoặc bảng vào database báo cáo chung, gửi event tới kho dữ liệu, chạy báo cáo từng tenant rồi gộp kết quả (khi số tenant nhỏ), và giữ metric sản phẩm tách khỏi dữ liệu khách hàng.

Chi phí vận hành là yếu tố lớn khác: nhiều database đồng nghĩa nhiều backup, upgrade, monitoring và phản ứng sự cố. Bạn cũng có thể gặp giới hạn kết nối nhanh hơn vì mỗi tenant cần pool kết nối riêng.

Sai lầm phổ biến gây rò rỉ dữ liệu hoặc đau đầu sau này

Test Isolation Early
Thiết lập hai tenant và chạy các bài kiểm tra phá vỡ trước khi cố định schema.
Xây prototype

Hầu hết vấn đề multi-tenant không phải là thất bại “thiết kế lớn”. Là những thiếu sót nhỏ dần thành lỗ hổng bảo mật, báo cáo lộn xộn và cleanup tốn kém. Multi-tenancy chỉ hoạt động khi tách tenant được coi là thói quen, không phải tính năng thêm vào sau.

Một rò rỉ thường thấy là quên trường tenant trên một bảng, đặc biệt các bảng join như user_roles, invoice_items, hoặc tags. Mọi thứ có vẻ ổn cho đến khi một truy vấn hoặc tìm kiếm join qua bảng đó và kéo dòng của tenant khác.

Vấn đề khác là dashboard admin bỏ qua lọc tenant. Thường bắt đầu là “chỉ cho support”, rồi được tái sử dụng. Công cụ no-code không thay đổi rủi ro: mọi truy vấn, business process và endpoint đọc dữ liệu tenant đều cần cùng phạm vi tenant.

ID cũng gây rắc rối. Nếu bạn chia sẻ ID dễ đọc giữa tenant (ví dụ order_number = 1001) và cho rằng chúng là duy nhất toàn cục, công cụ support và tích hợp sẽ trộn lẫn bản ghi. Giữ định danh theo tenant khác với primary key nội bộ, và luôn bao gồm ngữ cảnh tenant trong lookup.

Cuối cùng, các đội thường đánh giá thấp migration và backup khi mở rộng. Việc dễ với 10 tenant có thể chậm và rủi ro với 1.000.

Kiểm tra nhanh phòng hầu hết đau:

  • Làm rõ ownership tenant trên mọi bảng, kể cả join tables.
  • Dùng một pattern scoping tenant và tái sử dụng khắp nơi.
  • Đảm bảo báo cáo và export không chạy nếu thiếu scope tenant (trừ khi thực sự toàn cục).
  • Tránh ID mơ hồ tenant trong API và công cụ support.
  • Thực hành restore và migration sớm, không chờ tăng trưởng.

Ví dụ: một support agent tìm “invoice 1001” và kéo nhầm tenant vì lookup bỏ qua scope tenant. Là bug nhỏ nhưng hậu quả lớn.

Checklist nhanh trước khi chốt

Trước khi cố định mô hình đa-tenant, hãy chạy vài test. Mục tiêu là phát hiện rò rỉ sớm và xác nhận lựa chọn còn hoạt động khi bảng lớn.

Kiểm tra nhanh làm trong một ngày

  • Bằng chứng tách dữ liệu: tạo hai tenant (A và B), thêm bản ghi giống nhau, rồi xác minh mọi thao tác đọc và cập nhật đều có scope tenant. Đừng chỉ dựa vào bộ lọc UI.
  • Test phá quyền: đăng nhập như user của Tenant A và cố mở, sửa hoặc xoá bản ghi của Tenant B chỉ bằng cách đổi ID. Nếu có gì thành công, coi đó là blocker.
  • Đường ghi an toàn: xác nhận bản ghi mới luôn có giá trị tenant đúng (hoặc vào schema/database đúng), kể cả khi tạo qua job nền, import hoặc automation.
  • Thử báo cáo: xác nhận bạn có thể làm báo cáo chỉ theo tenant và báo cáo “tất cả tenant” (cho staff nội bộ), với quy tắc rõ ai được xem toàn cục.
  • Kiểm tra hiệu năng: đặt chiến lược index ngay (nhất là (tenant_id, created_at) và các filter thường gặp), và cố ý đo một truy vấn chậm để biết thế nào là “tệ”.

Để làm bài kiểm tra báo cáo cụ thể, chọn hai câu hỏi bạn biết sẽ cần (một theo tenant, một toàn cục) và chạy trên dữ liệu mẫu.

-- Tenant-only: last 30 days, one tenant
SELECT count(*)
FROM tickets
WHERE tenant_id = :tenant_id
  AND created_at >= now() - interval '30 days';

-- Global (admin): compare tenants
SELECT tenant_id, count(*)
FROM tickets
WHERE created_at >= now() - interval '30 days'
GROUP BY tenant_id;

Nếu bạn prototype trong AppMaster, xây các kiểm tra này vào Business Process (read, write, delete), và seed hai tenant trong Data Designer. Khi các test này pass với dung lượng dữ liệu thực tế, bạn có thể quyết định với độ tin cậy cao.

Kịch bản ví dụ: từ khách hàng đầu tiên tới khi mở rộng

Avoid Tenant ID Collisions
Thêm quy tắc unique theo tenant để tránh trùng lặp ID và email giữa các khách hàng.
Bắt đầu xây dựng

Một công ty 20 người ra portal cho khách hàng: invoices, tickets và dashboard đơn giản. Họ kỳ vọng 10 tenant trong tháng đầu, rồi tới 1.000 trong năm sau.

Giai đoạn đầu, mô hình đơn giản nhất thường là một database với mọi bảng dữ liệu khách có tenant_id. Nhanh để xây, dễ báo cáo và tránh lặp cấu hình.

Với 10 tenant, rủi ro lớn nhất không phải hiệu năng mà là quyền. Một bộ lọc quên tenant_id (ví dụ truy vấn “list invoices” quên tenant_id) có thể lộ dữ liệu. Đội nên enforce tenant check ở chỗ thống nhất (business logic chia sẻ hoặc pattern API tái sử dụng) và coi scoping tenant là không thể thương lượng.

Khi tăng từ 10 lên 1.000, nhu cầu thay đổi: báo cáo nặng hơn, support cần “export tất cả dữ liệu cho tenant này”, và vài tenant lớn bắt đầu chiếm tài nguyên và làm chậm bảng chung.

Lộ trình nâng cấp thực tế thường như sau:

  1. Giữ logic app và quy tắc quyền, nhưng chuyển các tenant có volume lớn sang schema riêng.
  2. Với các tenant lớn nhất (hoặc khách hàng tuân thủ nghiêm), chuyển họ sang database riêng.
  3. Giữ lớp báo cáo chung đọc từ mọi tenant và chạy báo cáo nặng ngoài giờ.

Chọn mô hình đơn giản nhất vẫn tách dữ liệu an toàn hôm nay, rồi lên kế hoạch di cư cho vấn đề “vài tenant khổng lồ” thay vì tối ưu hoá cho nó ngay từ ngày một.

Bước tiếp theo: chọn mô hình và prototype trong backend không cần code

Chọn dựa trên điều bạn cần bảo vệ trước: tách dữ liệu, đơn giản vận hành, hay khả năng scale theo tenant. Sự tự tin đến từ việc xây prototype nhỏ và cố gắng phá nó bằng các thử nghiệm quyền và báo cáo.

Hướng dẫn bắt đầu đơn giản:

  • Nếu hầu hết tenant nhỏ và bạn cần báo cáo xuyên-tenant đơn giản, bắt đầu với một database và tenant_id trên mọi dòng.
  • Nếu cần tách bạch mạnh hơn nhưng vẫn muốn quản lý một database, cân nhắc schema riêng cho từng tenant.
  • Nếu tenant yêu cầu tách biệt hoàn toàn (tuân thủ, backup riêng, nguy cơ noisy-neighbor), cân nhắc database riêng cho từng tenant.

Trước khi xây, viết ranh giới tenant bằng ngôn ngữ đơn giản. Định nghĩa roles (owner, admin, agent, viewer), quyền của từng role, và dữ liệu “toàn cục” là gì (plans, templates, audit logs). Quyết định cách báo cáo hoạt động: chỉ theo tenant hay “tất cả tenant” cho staff nội bộ.

Nếu dùng AppMaster, bạn có thể prototype nhanh các pattern này: mô hình bảng trong Data Designer (bao gồm tenant_id, ràng buộc unique và index truy vấn), rồi enforce quy tắc trong Business Process Editor để mọi đọc/ghi luôn có scope tenant. Nếu cần một điểm tham chiếu nền tảng, AppMaster là một ví dụ ở appmaster.io.

Bài kiểm tra thực tế cuối cùng: tạo hai tenant (A và B), thêm users và orders tương tự, và chạy cùng quy trình cho cả hai. Thử export báo cáo cho tenant A, rồi cố ý gửi ID của tenant B vào cùng endpoint. Prototype của bạn chỉ “đủ an toàn” khi những thử nghiệm đó thất bại mọi lần và các báo cáo chính vẫn chạy nhanh với dữ liệu thực tế.

Câu hỏi thường gặp

Which multi-tenant database model should I start with?

Mặc định chọn cơ sở dữ liệu đơn với tenant_id trên mọi bảng thuộc quyền sở hữu tenant nếu bạn muốn vận hành đơn giản và thường xuyên phân tích xuyên-tenant. Chuyển sang schema riêng khi bạn cần tách bạch mạnh hơn hoặc tuỳ biến theo tenant mà không chạy nhiều cơ sở dữ liệu. Chọn cơ sở dữ liệu riêng khi tuân thủ hoặc yêu cầu doanh nghiệp đòi hỏi tách biệt hoàn toàn và kiểm soát hiệu năng theo từng tenant.

How do I prevent accidental cross-tenant data leaks?

Xem việc gán tenant là bắt buộc ở backend, không phải bộ lọc UI. Làm cho tenant_id là bắt buộc trên các bảng thuộc tenant, và luôn lấy giá trị đó từ ngữ cảnh người dùng đã xác thực thay vì tin vào input từ client. Thêm tầng an toàn như row-level security của PostgreSQL nếu phù hợp, và viết test cố tình truy cập bản ghi của tenant khác chỉ bằng cách thay đổi ID.

What indexes matter most in a single database with tenant_id?

Đặt tenant_id ở đầu các index khớp với bộ lọc thường dùng để database có thể nhảy trực tiếp tới phần dữ liệu của một tenant. Một cấu hình cơ bản là index (tenant_id, created_at) cho các view theo thời gian và thêm (tenant_id, status) hoặc (tenant_id, user_id) cho các bộ lọc dashboard thường gặp. Đồng thời đảm bảo các ràng buộc unique là theo tenant, ví dụ “email unique theo tenant”.

What do I gain and lose with separate schemas per tenant?

Schema riêng giảm rủi ro join chéo tenant vì bảng nằm trong namespace khác nhau, và bạn có thể đặt quyền ở mức schema. Nhược điểm chính là migration: mỗi schema cần thay đổi tương tự, và điều này trở thành vấn đề vận hành khi số tenant tăng. Nó là lựa chọn trung gian tốt khi muốn tách bạch hơn so với tenant_id nhưng vẫn quản lý một database duy nhất.

When is a separate database per tenant actually worth it?

Cơ sở dữ liệu riêng giảm diện phát tán lỗi: spike về hiệu năng, cấu hình sai hoặc hỏng dữ liệu thường chỉ ảnh hưởng một tenant. Chi phí là vận hành nhiều hơn: provisioning, backup, monitoring và migrations nhân lên theo số tenant. Bạn cũng cần thư mục tenant đáng tin cậy và cơ chế điều hướng request để mọi gọi API dùng đúng kết nối database.

How do I handle “all tenants” reporting if data is split by schema or database?

Báo cáo xuyên-tenant dễ nhất với cơ sở dữ liệu đơn và tenant_id, vì dashboard toàn cục chỉ là các truy vấn không có bộ lọc tenant. Với schema hoặc database riêng, phân tích toàn cục thường thực hiện bằng cách sao chép các event hoặc bảng tóm tắt vào một kho báo cáo chung theo lịch. Nguyên tắc đơn giản: metric toàn sản phẩm vào lớp báo cáo chung, trong khi dữ liệu tenant giữ tách biệt.

What’s the safest way to let support staff access tenant accounts?

Hiển thị rõ ngữ cảnh tenant trong công cụ hỗ trợ và yêu cầu chuyển tenant một cách có chủ ý trước khi xem bản ghi. Nếu dùng impersonation, ghi log ai đã truy cập gì và khi nào, và đặt thời hạn dùng. Tránh workflow hỗ trợ chấp nhận một ID bản ghi mà không có ngữ cảnh tenant, bởi đó là con đường dẫn tới lỗi như “invoice 1001” lấy nhầm khách hàng.

How do I support tenant customization without making the model messy?

Nếu tenant cần trường hoặc luồng khác nhau, schema hoặc database riêng giảm khả năng thay đổi của một tenant ảnh hưởng tới những người khác. Nếu hầu hết tenant giống nhau, giữ model chung với tenant_id và xử lý khác biệt bằng tuỳ chọn cấu hình như feature flags hoặc trường tuỳ chọn. Chìa khóa là tránh các bảng “gần như toàn cục” lẫn lộn ý nghĩa chia sẻ và riêng theo tenant mà không có ownership rõ ràng.

How do I implement multi-tenancy safely in a no-code backend like AppMaster?

Xác định ranh giới tenant sớm: quyết định lưu ngữ cảnh tenant ở đâu sau khi xác thực và đảm bảo mọi thao tác đọc/ghi dùng nó. Trong AppMaster, thường có nghĩa là gán tenant_id từ người dùng đã xác thực trong Business Process trước khi tạo hoặc truy vấn bản ghi thuộc tenant, để endpoint không thể quên nó. Xử lý này như một pattern có thể tái sử dụng, không phải thứ viết lại cho mỗi màn hình.

What tests should I run before committing to a multi-tenant model?

Tạo hai tenant có dữ liệu tương tự và cố gắng phá vỡ isolation chỉ bằng cách đổi ID bản ghi khi đọc, cập nhật và xoá. Xác nhận background jobs, imports và exports vẫn ghi vào đúng phạm vi tenant, vì những đường này dễ bị bỏ sót. Chạy một báo cáo ở mức tenant và một báo cáo admin toàn cục trên dữ liệu mẫu có dung lượng thực tế để kiểm tra hiệu năng và quyền truy cập.

Dễ dàng bắt đầu
Tạo thứ gì đó tuyệt vời

Thử nghiệm với AppMaster với gói miễn phí.
Khi bạn sẵn sàng, bạn có thể chọn đăng ký phù hợp.

Bắt đầu