Mọi ứng dụng hay website khi trải qua giai đoạn phát triển mạnh mẽ đều cần phải mở rộng quy mô để đáp ứng nhu cầu lưu lượng truy cập ngày càng cao. Đối với các ứng dụng và website phụ thuộc vào dữ liệu, việc mở rộng không chỉ cần đảm bảo hiệu suất mà còn phải bảo vệ an toàn và tính toàn vẹn của dữ liệu. Vì rất khó để dự đoán chính xác mức độ phổ biến và thời gian duy trì sự phát triển của một website hoặc ứng dụng, nhiều tổ chức chọn lựa kiến trúc cơ sở dữ liệu có khả năng mở rộng linh hoạt.
DataOnline sẽ giúp bạn hiểu một kiến trúc cơ sở dữ liệu được thiết kế để mở rộng như vậy: database sharding. Mặc dù sharding đã trở thành một chủ đề được quan tâm nhiều trong những năm gần đây, nhưng vẫn còn nhiều người chưa nắm rõ khái niệm này và không biết khi nào nên áp dụng sharding trong cơ sở dữ liệu.
Database sharding đòi hỏi hạ tầng mạnh mẽ để đảm bảo hiệu suất và tính ổn định. Với VPS hosting, bạn có thể dễ dàng mở rộng hệ thống, hỗ trợ phân vùng dữ liệu mượt mà. Khám phá các gói VPS hosting tại DataOnline để tối ưu hóa cơ sở dữ liệu của bạn ngay hôm nay!
Chúng ta sẽ cùng nhau khám phá khái niệm sharding, những lợi ích và hạn chế của nó, cũng như một số phương pháp sharding phổ biến hiện nay.
Sharding là gì?
Sharding là một mẫu kiến trúc cơ sở dữ liệu liên quan đến việc phân vùng theo chiều ngang — đó là việc tách các dòng của một bảng ra thành nhiều bảng riêng biệt, được gọi là các phân vùng (partitions). Mỗi phân vùng có cùng một sơ đồ (schema) và các cột giống nhau, nhưng chứa các dòng dữ liệu hoàn toàn khác nhau. Dữ liệu ở mỗi phân vùng là độc lập và không phụ thuộc vào dữ liệu của các phân vùng khác.
Có thể hình dung phân vùng theo chiều ngang như một dạng đối chiếu với phân vùng theo chiều dọc. Trong bảng phân vùng theo chiều dọc, toàn bộ các cột sẽ được tách riêng và đưa vào các bảng mới, riêng biệt. Dữ liệu trong mỗi phân vùng theo chiều dọc là độc lập, mỗi bảng chứa các dòng và cột riêng biệt. Sơ đồ dưới đây minh họa cách một bảng có thể được phân vùng theo cả chiều ngang và chiều dọc:
Sharding bao gồm việc chia nhỏ dữ liệu thành hai phần trở lên, gọi là các logical shards. Các logical shards sau đó được phân phối trên các nút cơ sở dữ liệu riêng biệt, được gọi là physical shards, và mỗi physical shard có thể chứa nhiều logical shards. Dù được chia ra như vậy, dữ liệu của tất cả các shards khi kết hợp lại vẫn đại diện cho một tập dữ liệu logic hoàn chỉnh.
Các database shards là điển hình của kiến trúc shared-nothing. Điều này có nghĩa là các shards hoàn toàn tự chủ; chúng không chia sẻ bất kỳ dữ liệu hay tài nguyên tính toán nào với nhau. Trong một số trường hợp, có thể cần nhân bản một số bảng vào từng shard để phục vụ như các bảng tham chiếu. Ví dụ, giả sử bạn có một cơ sở dữ liệu cho ứng dụng phụ thuộc vào tỉ lệ chuyển đổi cố định cho đơn vị đo khối lượng. Bằng cách nhân bản bảng chứa dữ liệu tỉ lệ chuyển đổi cần thiết vào từng shard, bạn sẽ đảm bảo rằng tất cả dữ liệu cần thiết cho các truy vấn đều có sẵn tại mọi shard.
Thông thường, sharding được thực hiện ở cấp độ ứng dụng, có nghĩa là mã nguồn của ứng dụng sẽ xác định shard nào sẽ nhận các thao tác đọc và ghi. Tuy nhiên, một số hệ quản trị cơ sở dữ liệu tích hợp sẵn khả năng sharding, cho phép bạn thực hiện sharding trực tiếp ở cấp độ cơ sở dữ liệu.
Với cái nhìn tổng quát về sharding, hãy cùng xem xét một số ưu điểm và nhược điểm của kiến trúc cơ sở dữ liệu này.
Ưu điểm của Sharding
Lợi ích chính khi sharding cơ sở dữ liệu là giúp mở rộng theo chiều ngang, hay còn gọi là scaling out. Scaling out là việc thêm nhiều máy chủ vào hệ thống hiện có để phân tán tải, từ đó cho phép xử lý lưu lượng truy cập lớn hơn và tăng tốc độ xử lý. Điều này thường được so sánh với vertical scaling (scaling up), tức là nâng cấp phần cứng của máy chủ hiện có (thêm RAM, CPU, v.v.).
Việc chạy một cơ sở dữ liệu quan hệ trên một máy chủ đơn lẻ và mở rộng bằng cách nâng cấp tài nguyên là khá đơn giản. Tuy nhiên, bất kỳ cơ sở dữ liệu không phân tán nào cũng sẽ bị giới hạn về dung lượng lưu trữ và sức mạnh tính toán, vì vậy khả năng mở rộng theo chiều ngang sẽ mang lại sự linh hoạt vượt trội cho hệ thống của bạn.
Một lý do khác khiến nhiều người chọn kiến trúc cơ sở dữ liệu sharded là để tăng tốc độ phản hồi truy vấn. Khi bạn gửi một truy vấn trên một cơ sở dữ liệu chưa được sharding, hệ thống có thể phải quét qua tất cả các dòng trong bảng để tìm ra kết quả cần thiết. Đối với một ứng dụng với cơ sở dữ liệu khổng lồ và đơn khối, truy vấn có thể trở nên quá chậm. Bằng cách sharding một bảng thành nhiều bảng nhỏ hơn, số dòng cần quét mỗi lần sẽ giảm, giúp truy vấn trả về kết quả nhanh hơn rất nhiều.
Sharding cũng có thể giúp ứng dụng trở nên đáng tin cậy hơn bằng cách giảm thiểu tác động của các sự cố. Nếu ứng dụng hoặc website của bạn dựa trên một cơ sở dữ liệu không sharded, một sự cố có thể làm cho toàn bộ ứng dụng không hoạt động. Ngược lại, với cơ sở dữ liệu được sharding, một sự cố thường chỉ ảnh hưởng đến một shard đơn lẻ. Dù có thể làm cho một số phần của ứng dụng hoặc website không khả dụng đối với một số người dùng, nhưng tác động tổng thể vẫn nhỏ hơn nhiều so với việc toàn bộ cơ sở dữ liệu gặp sự cố.
Nhược điểm của Sharding
Mặc dù sharding có thể làm cho việc mở rộng trở nên dễ dàng hơn và cải thiện hiệu suất, nó cũng có những hạn chế nhất định. Dưới đây là một số vấn đề mà bạn có thể gặp phải khi áp dụng sharding:
-
Độ phức tạp cao: Việc triển khai một kiến trúc cơ sở dữ liệu sharded đúng cách là một thách thức lớn. Nếu thực hiện không đúng, quá trình sharding có thể dẫn đến mất mát dữ liệu hoặc bảng bị hỏng. Ngay cả khi triển khai đúng, sharding cũng sẽ ảnh hưởng lớn đến quy trình làm việc của nhóm bạn. Thay vì truy cập và quản lý dữ liệu từ một điểm duy nhất, người dùng phải quản lý dữ liệu trên nhiều vị trí shard, điều này có thể gây gián đoạn cho một số nhóm.
-
Mất cân bằng dữ liệu: Một vấn đề thường gặp sau khi sharding là các shard có thể dần trở nên mất cân bằng. Ví dụ, giả sử bạn có cơ sở dữ liệu được chia thành hai shard, một chứa khách hàng có họ bắt đầu từ chữ A đến M và một chứa khách hàng từ N đến Z. Nếu ứng dụng của bạn phục vụ quá nhiều khách hàng có họ bắt đầu bằng chữ G, shard A-M sẽ tích lũy nhiều dữ liệu hơn shard N-Z, dẫn đến tình trạng nghẽn cổ chai (database hotspot) và làm chậm hệ thống. Khi đó, bất kỳ lợi ích nào từ việc sharding cũng có thể bị bù trừ bởi các sự cố chậm trễ và treo hệ thống, đòi hỏi phải khắc phục và tái sharding lại để đạt phân phối dữ liệu đồng đều.
-
Khó khăn trong việc khôi phục cấu trúc ban đầu: Một khi cơ sở dữ liệu đã được sharding, việc chuyển đổi trở lại thành kiến trúc không sharded là vô cùng khó khăn. Các bản sao lưu (backup) trước khi sharding sẽ không bao gồm dữ liệu được ghi sau khi phân vùng, vì vậy để khôi phục cấu trúc ban đầu, bạn phải hợp nhất dữ liệu mới với các bản sao lưu cũ hoặc chuyển đổi lại cơ sở dữ liệu phân vùng thành một cơ sở dữ liệu đơn lẻ – cả hai đều tốn kém và mất thời gian.
-
Hỗ trợ tự động hạn chế: Cuối cùng, sharding không được hỗ trợ tự động bởi tất cả các engine cơ sở dữ liệu. Ví dụ, PostgreSQL không tích hợp sẵn tính năng sharding tự động mặc dù có thể thực hiện sharding thủ công. Một số nhánh (fork) của PostgreSQL có hỗ trợ tự động sharding, nhưng thường không theo kịp phiên bản mới nhất và thiếu một số tính năng quan trọng. Một số công nghệ cơ sở dữ liệu chuyên biệt — chẳng hạn như MySQL Cluster hay các dịch vụ cơ sở dữ liệu như MongoDB Atlas — có tích hợp tính năng auto-sharding, nhưng các phiên bản vanilla của các hệ quản trị cơ sở dữ liệu này thì không. Vì lý do này, sharding thường đòi hỏi phải tự triển khai (“roll your own”), đồng nghĩa với việc việc tìm kiếm tài liệu hay mẹo xử lý sự cố liên quan đến sharding sẽ gặp nhiều khó khăn.
Những hạn chế nêu trên chỉ là một số vấn đề chung cần cân nhắc trước khi thực hiện sharding. Tùy thuộc vào từng trường hợp sử dụng cụ thể, có thể còn tồn tại nhiều nhược điểm khác cần xem xét.
Giờ đây, sau khi đã đề cập đến một số ưu và nhược điểm của sharding, chúng ta hãy cùng xem qua một số kiến trúc sharding phổ biến.
Kiến trúc Sharding
Khi bạn đã quyết định sharding cơ sở dữ liệu, bước tiếp theo là xác định cách thức thực hiện. Khi chạy truy vấn hay phân phối dữ liệu đến các bảng hay cơ sở dữ liệu được sharding, điều quan trọng là phải đảm bảo dữ liệu được gửi đến đúng shard. Nếu không, có thể dẫn đến mất mát dữ liệu hoặc truy vấn chậm chạp. Phần này sẽ giới thiệu một vài kiến trúc sharding phổ biến, mỗi kiến trúc sử dụng một phương thức khác nhau để phân phối dữ liệu qua các shard.
Sharding dựa trên Key
Sharding dựa trên key, còn được gọi là sharding dựa trên hàm băm (hash based sharding), bao gồm việc sử dụng một giá trị được lấy từ dữ liệu mới ghi vào — chẳng hạn như số ID của khách hàng, địa chỉ IP của ứng dụng khách, một mã bưu điện, v.v. — sau đó đưa giá trị đó vào một hàm băm để xác định shard nào sẽ chứa dữ liệu đó. Một hàm băm là hàm nhận đầu vào là một phần dữ liệu (ví dụ, email của khách hàng) và trả về một giá trị rời rạc, được gọi là hash value. Trong trường hợp sharding, giá trị băm này được sử dụng như một shard ID để xác định shard nào sẽ lưu trữ dữ liệu mới.
Quy trình tổng quát như sau:
Để đảm bảo rằng các bản ghi được đặt vào đúng shard và một cách nhất quán, các giá trị đưa vào hàm băm cần được lấy từ cùng một cột. Cột này được gọi là shard key. Một cách đơn giản, shard key tương tự như primary key ở chỗ nó được sử dụng để xác định duy nhất cho từng dòng dữ liệu. Nói chung, một shard key nên có tính tĩnh (static), tức là không chứa các giá trị có thể thay đổi theo thời gian, vì điều đó sẽ làm tăng khối lượng công việc của các thao tác cập nhật và có thể làm chậm hiệu suất.
Mặc dù sharding dựa trên key là một kiến trúc phổ biến, nhưng nó có thể gây khó khăn khi cần thêm hay bớt các máy chủ vào cơ sở dữ liệu một cách linh động. Khi bạn thêm máy chủ, mỗi máy sẽ cần có một giá trị băm tương ứng và nhiều bản ghi hiện có, nếu không phải tất cả, sẽ cần được ánh xạ lại với giá trị băm mới và sau đó di chuyển sang máy chủ thích hợp. Khi quá trình cân bằng lại dữ liệu bắt đầu, cả hàm băm mới lẫn cũ đều không còn phù hợp, khiến máy chủ không thể ghi dữ liệu mới trong quá trình di chuyển và ứng dụng có thể gặp thời gian gián đoạn.
Ưu điểm lớn của chiến lược này là nó có thể được dùng để phân phối dữ liệu đều nhằm ngăn chặn hiện tượng nghẽn cổ chai. Hơn nữa, vì dữ liệu được phân phối theo thuật toán nên không cần phải duy trì một bản đồ (map) để biết dữ liệu ở đâu, khác với các chiến lược khác như sharding dựa trên range hay dựa trên directory.
Sharding dựa trên phạm vi
Sharding dựa trên phạm vi bao gồm việc chia dữ liệu dựa trên các khoảng giá trị nhất định. Ví dụ, giả sử bạn có một cơ sở dữ liệu lưu trữ thông tin về tất cả các sản phẩm trong danh mục của một nhà bán lẻ. Bạn có thể tạo ra một vài shard khác nhau và chia nhỏ thông tin của mỗi sản phẩm dựa vào khoảng giá mà sản phẩm đó thuộc về, như minh họa:
Ưu điểm chính của sharding dựa trên phạm vi là nó khá đơn giản để triển khai. Mỗi shard chứa một tập dữ liệu khác nhau nhưng tất cả đều có cùng một sơ đồ như cơ sở dữ liệu gốc. Mã ứng dụng sẽ xác định phạm vi dữ liệu thuộc về shard nào và ghi dữ liệu vào shard tương ứng.
Tuy nhiên, nhược điểm của phương pháp này là nó không ngăn chặn được sự phân phối dữ liệu không đồng đều, dẫn đến hiện tượng “hotspot” trong cơ sở dữ liệu. Nhìn vào sơ đồ ví dụ, ngay cả khi mỗi shard chứa một lượng dữ liệu tương đương, khả năng cao là một số sản phẩm sẽ nhận được nhiều sự quan tâm hơn so với các sản phẩm khác, dẫn đến việc các shard tương ứng bị quá tải về số lượng truy vấn đọc.
Sharding dựa trên bảng tra cứu
Để triển khai sharding dựa trên bảng tra cứu, bạn cần tạo và duy trì một bảng tra cứu sử dụng shard key để theo dõi xem dữ liệu được lưu trữ ở shard nào. Bảng tra cứu là một bảng chứa tập hợp thông tin tĩnh về vị trí của dữ liệu cụ thể. Sơ đồ dưới đây minh họa ví dụ đơn giản về directory based sharding:
Ở đây, cột “Delivery Zone” được định nghĩa là shard key. Dữ liệu từ shard key sẽ được ghi vào bảng tra cứu kèm theo thông tin cho biết dòng dữ liệu đó nên được ghi vào shard nào. Phương pháp này tương tự như sharding dựa trên phạm vi, nhưng thay vì xác định khoảng giá trị mà dữ liệu thuộc về, mỗi key sẽ được liên kết với một shard cụ thể. Directory based sharding là lựa chọn tốt hơn so với sharding dựa trên phạm vi khi shard key có độ phân giải thấp (low cardinality) — nghĩa là có ít giá trị khả dĩ — và không hợp lý khi một shard chứa một dải các key. Lưu ý rằng, khác với sharding dựa trên key, phương pháp này không đưa shard key qua hàm băm mà chỉ tra cứu trong bảng để xác định nơi cần ghi dữ liệu.
Ưu điểm lớn của directory based sharding là tính linh hoạt. Kiến trúc sharding dựa trên phạm vi giới hạn bạn chỉ có thể xác định các khoảng giá trị, trong khi sharding dựa trên key lại bị ràng buộc bởi hàm băm cố định, vốn khó thay đổi sau này. Ngược lại, directory based sharding cho phép bạn sử dụng bất kỳ hệ thống hay thuật toán nào để phân bổ dữ liệu vào các shard, và việc thêm shard mới một cách động cũng trở nên dễ dàng hơn.
Tuy nhiên, nhược điểm của phương pháp này là việc phải kết nối tới bảng tra cứu trước mỗi truy vấn hoặc thao tác ghi có thể ảnh hưởng tiêu cực đến hiệu suất của ứng dụng. Hơn nữa, bảng tra cứu có thể trở thành điểm nghẽn duy nhất (single point of failure): nếu bảng này bị hỏng hoặc gặp sự cố, sẽ ảnh hưởng đến khả năng ghi dữ liệu mới hoặc truy xuất dữ liệu hiện có.
Có nên Shard không?
Việc có nên triển khai kiến trúc cơ sở dữ liệu sharded hay không luôn là vấn đề gây tranh cãi. Một số người coi sharding là điều tất yếu đối với các cơ sở dữ liệu đạt đến một quy mô nhất định, trong khi người khác lại xem đó như một “cái đầu đau” nên tránh trừ khi thực sự cần thiết, do độ phức tạp vận hành mà sharding mang lại.
Với độ phức tạp gia tăng đó, sharding thường chỉ được áp dụng khi dữ liệu xử lý có khối lượng rất lớn. Dưới đây là một số tình huống thường gặp mà sharding có thể mang lại lợi ích:
-
Lượng dữ liệu của ứng dụng phát triển vượt quá khả năng lưu trữ của một nút cơ sở dữ liệu đơn lẻ.
-
Khối lượng thao tác ghi hoặc đọc dữ liệu vượt quá khả năng xử lý của một nút hoặc các bản sao đọc (read replicas), dẫn đến thời gian phản hồi chậm hoặc hết thời gian chờ.
-
Băng thông mạng yêu cầu của ứng dụng vượt quá khả năng băng thông của một nút cơ sở dữ liệu đơn lẻ và các bản sao đọc, gây ra hiện tượng chậm trễ hoặc hết thời gian chờ.
Trước khi quyết định sharding, bạn nên xem xét hết tất cả các tùy chọn tối ưu hóa cơ sở dữ liệu khác. Một số tối ưu hóa bạn có thể cân nhắc bao gồm:
-
Thiết lập cơ sở dữ liệu từ xa: Nếu ứng dụng của bạn là một hệ thống đơn khối với tất cả các thành phần nằm trên cùng một máy chủ, bạn có thể cải thiện hiệu suất của cơ sở dữ liệu bằng cách chuyển nó sang một máy riêng. Phương án này không làm tăng độ phức tạp như sharding vì các bảng của cơ sở dữ liệu vẫn nguyên vẹn, nhưng vẫn cho phép bạn mở rộng theo chiều dọc riêng biệt.
-
Triển khai caching: Nếu hiệu suất đọc của ứng dụng là vấn đề chính, caching có thể giúp cải thiện bằng cách lưu tạm thời dữ liệu đã được truy xuất vào bộ nhớ, cho phép truy cập nhanh hơn trong tương lai.
-
Tạo các bản sao đọc (read replicas): Đây là chiến lược khác giúp cải thiện hiệu suất đọc, bằng cách sao chép dữ liệu từ máy chủ chính (primary server) sang một hoặc nhiều máy chủ phụ (secondary servers). Tất cả các thao tác ghi đều được gửi đến máy chủ chính trước khi sao chép sang máy phụ, trong khi các thao tác đọc chỉ diễn ra trên các máy phụ. Việc phân phối tải giữa các máy chủ giúp tránh tình trạng quá tải của một máy đơn lẻ.
-
Nâng cấp máy chủ: Trong hầu hết các trường hợp, việc nâng cấp máy chủ cơ sở dữ liệu với nhiều tài nguyên hơn đòi hỏi ít công sức hơn so với sharding. Tuy nhiên, một máy chủ được nâng cấp với nhiều tài nguyên hơn cũng có thể tốn kém hơn.
Nhớ rằng, nếu ứng dụng hoặc website của bạn phát triển vượt quá một mức nhất định, không có chiến lược tối ưu hóa nào có thể cải thiện hiệu suất một cách độc lập. Trong những trường hợp như vậy, sharding có thể chính là giải pháp tối ưu nhất.