Gần đây mình tham gia phát triển một dịch vụ kiểu Multi-tenant Software as a Service nên cũng dành khá nhiều thời gian tìm hiểu cách thiết kế DB cho nó. Theo nhận định chủ quan thì kiểu ứng dụng này thời gian tới sẽ ngày càng phổ biến. Thực tế lâu nay đã có khá nhiều phần mềm web based theo kiểu multitenancy này rồi.

Để có cái nhìn tổng quan về multi-tenant, giờ hãy tưởng tượng bạn có một web app, bạn muốn bán nó cho vài công ty khác cùng sử dụng và đóng logo của họ lên đó. Cách cài đặt đơn giản nhất là copy thành nhiều bản sao, mỗi ông dùng một bản độc lập nhau. Cứ có khách hàng mới là lại copy cả code, cà database ra rồi cài riêng cho ông khách đó, trên 1 hay nhiều server tùy bạn. Vấn đề nảy sinh khi bạn cần update hay sửa lỗi gì gì đó, bạn sẽ phải xử lý trên từng bản sao, cho từng khách hàng một. Công việc update đó lặp đi lặp lại, càng nhiều khách thì càng nhàm chán và dễ sai sót. Đến lúc này thì bạn nên cân nhắc biến ứng dụng SaaS của mình thành dạng multi-tenant.

Multi-tenancy nghĩa là có nhiều tenant (khách thuê – có thể là công ty, tổ chức) với nhiều user sẽ cùng sử dụng phần mềm của bạn. Khi làm app multi tenant, bạn chỉ triển khai nó một lần chứ không phải là sao chép ra một mớ. Bạn có thể update app một phát cho cả đám tenant và việc triển khai dịch vụ cũng đơn giản hơn. Nói chung multi tenant bây giờ thành xu thế rồi, chả tránh được. Ngày xưa làm phần mềm ra xong đem cài lên máy của khách rồi chạy đi chạy lại bảo trì, càng lắm khách thì càng chạy nhiều. Rồi đến lúc chuyển lên web, thì sao ra cho mỗi ông 1 virtual host hoặc server riêng, không phải chạy nhưng quản lý cũng mệt chả kém. Bây giờ làm kiểu multi tenant thì có thể chỉ cần deploy 1 lần lên 1 đám server, nhưng làm DB, code kiếc lại thêm phức tạp.

Phức tạp là vì tất cả khách cùng chạy chung server, chung code, có thể chung cả DB. Vậy làm sao để đảm bảo cô lập dữ liệu các khách hàng với nhau? Cứ chạy riêng rẽ như xưa thì dễ rồi, ông nào biết ông đấy thôi! Bài viết này mình sẽ giới thiệu 3 cách thiết kế Database – thành phần quan trọng nhất – mà mình lượm lặt được từ Internet. Phần này sẽ chỉ nói về cách thiết kế DB, code sẽ được giới thiệu trong các bài tiếp theo.

Thiết kế cơ sở dữ liệu cho Multi-Tenant SaaS Application

Như đã đề cập, Database là thành phần quan trọng nhất trong kiến trúc multi-tenant. Có 3 vấn đề cần quan tâm khi thiết kế DB:

  • Mức độ cô lập dữ liệu (tenant data isolation)
  • Khả năng sao lưu và phục hồi từng tenant
  • Khả năng mã hóa dữ liệu tenant

Bạn có thể chọn 1 trong 3 cách sau để xây dựng DB cho ứng dụng của mình.

  1. Mỗi tenant dùng một database riêng biệt.
  2. Dùng chung database nhưng chia cho mỗi tenant một schema riêng hoặc table riêng (nếu DB bạn chọn không có schema).
  3. Dùng chung tất, cả database lẫn schema hay table

Giờ chúng ta sẽ bàn chi tiết từng thiết kế.

Mỗi Tenant một Database

Ưu điểm lớn nhất của cách làm này là đảm bảo mức độ an toàn dữ liệu cao nhất cho từng tenant. Database được cô lập ở mức tối đa, nếu cần có thể đặt trên các server khác nhau, do đó tenant này không thể truy cập được dữ liệu của tenant khác.

Ưu điểm thứ hai là tính linh hoạt. Thử nghĩ coi, dữ liệu của doanh nghiệp chính là tài sản, bạn nắm giữ tài sản của họ. Nếu tenant nào đó muốn bạn mã hóa dữ liệu (để tránh rò rỉ do bạn bị tấn công hoặc do nhân viên của bạn lấy đi), trong khi người khác lại không có nhu cầu. Bạn sẽ không tốn chi phí để mã hóa toàn bộ, chỉ cần thực hiện trên tenant cụ thể vì chúng đã hoàn toàn độc lập với nhau.

Việc độc lập database cũng giúp việc sao lưu và phục hồi dữ liệu dễ dàng hơn bao giờ hết. Bạn có thể backup riêng tenant db hàng ngày thậm chí hàng giờ, tùy thuộc gói dịch vụ mà khách hàng đã mua, khi cần thì restore quá sức dễ dàng.

Với chừng ấy lợi ích thì nghe có vẻ đây là giải pháp ngon ăn. Nhưng giả sử bạn không có khách hàng nào VIP đến độ cần mã hóa và backup hàng giờ, thì việc triển khai này lại trở nên tốn kém. Cứ cho là dữ liệu còn bé, có thể chạy chung DB server, nhưng nó vẫn bới thêm công việc quản trị, vận hành…

Nhìn chung đây là một cách làm tốn kém, chỉ phù hợp nếu tập khách hàng có yêu cầu đặc biệt và sẵn sàng chịu chi. Giờ chúng ta nói về phương án rẻ hơn mà vẫn đảm bảo một phần tính cô lập dữ liệu: Mỗi Tenant một Schema riêng.

Mỗi Tenant một Schema

Cách làm này giảm đáng kể chi phí, app của bạn sẽ chỉ kết nối đến 1 Database duy nhất. Mỗi tenant sẽ có một schema riêng (với các table cần thiết) thay vì dùng cả database. Với những DBMS không hỗ trợ multi schema, bạn có thể triển khai bằng cách đặt tên table kèm theo tiền tố để phân biệt các tenant (VD: tenantA_product, tenantA_order…). Bằng cách này bạn có thể giảm bớt chi phí quản trị hoặc duy trì server.

Nhược điểm của thiết kế này là vấn đề backup – restore. Nếu nếu cần restore riêng 1 tenant mà DBMS không hỗ trợ restore từng schema, bạn sẽ phải restore toàn bộ DB, những người khác sẽ bị ảnh hưởng bởi downtime. Và để tránh việc restore ghi đè cả DB, bạn sẽ có nhiều việc cần phải làm! Chẳng hạn như restore vào 1 DB khác, lấy dữ liệu của schema cần xử lý ra, rồi bưng phần này vào DB đang chạy. Nghe thế chứ làm thật chưa chắc đã dễ!

Việc tách schema chỉ đảm bảo được một phần tính cô lập dữ liệu khách hàng nhưng là phương án rẻ hơn tách database. Cá nhân tôi thấy thiết kế này cực kì hợp lý, cân bằng giữa lợi ích, chi phí và độ phức tạp. Bây giờ ta chuyển qua cách thứ 3: Nhồi hết vào 1 chỗ!

Dùng chung Schema cho tất cả Tenant

Nếu bạn chọn DBMS không có schema thì tất cả sẽ dùng chung database. Cách này rất dễ triển khai, ít nhất là trong giai đoạn đầu của dự án, hoặc ở thời điềm mà bạn còn chưa từng nghĩ sẽ làm multi tenant! Vì dữ liệu của tất cả tenant sẽ được lưu trong cùng 1 bảng, bạn có thể chỉ cần thêm vào 1 trường để phân biệt tenant là đủ (như tenant_id chẳng hạn).

Làm kiểu này cũng có cái hay. Bạn không cần phải tạo schema rồi khởi tạo các table cho từng tenant, không cần lo quản lý schema nào của tenant nào, cũng chả phải can thiệp gì vào database.

Thế còn cái dở của mô hình này? Giờ nếu bạn làm ăn tốt, khách hàng tăng trưởng từng ngày, dữ liệu cũng tăng theo. Khi đó table của bạn sẽ ngày càng to ra, việc thao tác trên dữ liệu sẽ dần trở nên khó khăn và chậm chạp. Oái oăm là có những tenant ít dữ liệu lại chịu chung cảnh rùa bò với những thằng to. Điều này đem lại trải nghiệm không hay ho gì. Có nhiều cách để cải thiện hiệu năng nhưng dù sao cái gì cũng có giới hạn của nó. Đến lúc bạn sẽ phải tách vài ông to to ra chạy DB khác. Cũng không sao, miễn là giải quyết được vấn đề! Chỉ có điều việc lọc lấy dữ liệu để tách ra không phải một sớm một chiều mà xong ngay được, trong khi downtime thì có hạn! Lại còn cả vấn đề backup – restore nữa.

Túm lại thì thiết kế nào cũng có cái hay và cái rắc rối của nó. Bạn nên xem xét thấu đáo từng khía cạnh và đặt lên bàn cân để đưa ra lựa chọn cho riêng mình. Theo tôi đánh giá thì cách chia Schema là cân bằng nhất giữa mức độ an toàn, độ phức tạp khi triển khai và chi phí vận hàng database, tôi cũng đã chọn cách này cho vài dự án của mình. Còn bạn, bạn chọn phương án nào? Hoặc nếu cần thảo luận thêm, hãy để lại comment vì những điều trên chắc là chưa đủ.