Bất kỳ ứng dụng hay trang web nào khi phát triển mạnh đều sẽ đến lúc cần mở rộng để đáp ứng lưu lượng truy cập gia tăng. Đối với các ứng dụng và trang web dựa trên dữ liệu, việc mở rộng phải đảm bảo được tính bảo mật và tính toàn vẹn của dữ liệu.
Thật khó để dự đoán một trang web hay ứng dụng sẽ trở nên phổ biến đến mức nào hoặc duy trì sự phổ biến đó trong bao lâu, vì vậy một số tổ chức lựa chọn kiến trúc cơ sở dữ liệu cho phép mở rộng linh hoạt theo nhu cầu.
Trong bài viết này, chúng ta sẽ tìm hiểu Sharding là gì, một số lợi ích và hạn chế chính của nó cũng như một vài phương pháp sharding phổ biến.
Sharding là gì?
Sharding là một mẫu kiến trúc cơ sở dữ liệu liên quan đến horizontal partitioning (phân mảnh theo chiều ngang) – tức là việc tách các hàng của một bảng thành nhiều bảng khác nhau, được gọi là partition (phân vùng). Mỗi phân vùng này có cùng một cấu trúc bảng và các cột, nhưng chứa các hàng hoàn toàn khác nhau. Nói cách khác, dữ liệu trong mỗi phân vùng là duy nhất và độc lập với dữ liệu trong các phân vùng khác.
Để dễ hình dung, bạn có thể so sánh horizontal partitioning với vertical partitioning (phân mảnh theo chiều dọc). Trong vertical partitioning, toàn bộ các cột sẽ được tách ra và đưa vào các bảng riêng biệt. Dữ liệu trong từng vertical partition độc lập với dữ liệu ở các partition còn lại, và mỗi partition giữ cả các hà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 mảnh theo cả chiều ngang và chiều dọc:
Sharding bao gồm việc chia nhỏ dữ liệu thành hai hoặc nhiều phần nhỏ hơn, gọi là logical shards . Các logical shard này sau đó được phân phối trên các nút cơ sở dữ liệu riêng biệt, gọi là physical shards (shard vật lý), và mỗi physical shard có thể chứa nhiều logical shard. Mặc dù được chia nhỏ, nhưng toàn bộ dữ liệu trong các shard này vẫn đại diện cho toàn bộ tập dữ liệu logic đầy đủ.
Các database shard là ví dụ điển hình của kiến trúc shared-nothing – tức là các shard hoạt động độc lập, không chia sẻ dữ liệu hoặc tài nguyên xử lý với nhau. Tuy nhiên, trong một số trường hợp, có thể hợp lý khi sao chép một số bảng vào tất cả các shard để dùng làm bảng tham chiếu. Ví dụ, giả sử có một cơ sở dữ liệu cho ứng dụng phụ thuộc vào tỷ lệ quy đổi trọng lượng cố định. Khi sao chép bảng chứa dữ liệu tỷ lệ quy đổi vào từng shard, điều này sẽ giúp đả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 trong mỗi shard.
Trong nhiều trường hợp, sharding được triển khai ở tầng ứng dụng, tức là ứng dụng sẽ chứa mã định nghĩa shard nào sẽ được sử dụng cho các thao tác đọc và ghi. Tuy nhiên, một số hệ quản trị cơ sở dữ liệu (DBMS) hiện nay có hỗ trợ sharding built-in (tích hợp sẵn khả năng sharding), cho phép bạn triển khai trực tiếp ngay ở cấp độ cơ sở dữ liệu.
Sau phần tổng quan về sharding này, hãy cùng tìm hiểu một số ưu và nhược điểm chính của kiến trúc cơ sở dữ liệu này.
Lợi ích của Sharding
Lợi ích chính của việc sharding cơ sở dữ liệu là giúp hỗ trợ mở rộng theo chiều ngang (horizontal scaling), còn được gọi là scaling out. Horizontal scaling là việc thêm nhiều server vào hệ thống hiện tại để chia tải xử lý, từ đó cho phép xử lý nhiều lưu lượng hơn và tăng tốc độ xử lý. Điều này thường được so sánh với mở rộng theo chiều dọc (vertical scaling hoặc scaling up), tức là nâng cấp phần cứng của một máy chủ hiện có, thường bằng cách thêm RAM hoặc CPU.
Việc vận hành một cơ sở dữ liệu quan hệ trên một máy duy nhất và nâng cấp phần cứng khi cần là điều 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à khả năng xử lý, vì vậy việc có thể mở rộng theo chiều ngang giúp hệ thống linh hoạt hơn rất nhiều.
Một lý do khác khiến nhiều người chọn kiến trúc cơ sở dữ liệu dạng sharded là để tăng tốc độ phản hồi của truy vấn. Khi bạn thực hiện truy vấn trên một cơ sở dữ liệu không được sharded, hệ thống có thể phải tìm kiếm qua tất cả các hàng trong bảng để tìm ra kết quả mong muốn. Với các ứng dụng có cơ sở dữ liệu lớn và đơn khối (monolithic), các truy vấn có thể trở nên rất chậm. Tuy nhiên, khi bảng được chia thành nhiều phần (shards), truy vấn chỉ cần quét qua ít hàng hơn, giúp kết quả được trả về nhanh chóng hơn nhiều.
Sharding cũng có thể tăng tính tin cậy cho ứng dụng bằng cách giảm thiểu tác động của sự cố. Nếu ứng dụng hoặc trang web của bạn dựa vào một cơ sở dữ liệu chưa được sharded, bất kỳ sự cố nào xảy ra cũng có thể khiến toàn bộ ứng dụng ngừng hoạt động (outage). Nhưng với cơ sở dữ liệu đã sharded, một sự cố thường chỉ ảnh hưởng đến một shard duy nhất. Mặc dù điều này có thể khiến một số phần của ứng dụng hoặc trang web không khả dụng đối với một số người dùng, ảnh hưởng tổng thể vẫn sẽ ít nghiêm trọng hơn so với khi toàn bộ cơ sở dữ liệu bị sập.
Nhược điểm của Sharding
Mặc dù sharding cơ sở dữ liệu có thể giúp 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ó thể mang lại một số hạn chế nhất định. Dưới đây là một số nhược điểm và lý do tại sao bạn có thể nên tránh áp dụng sharding.
Khó khăn đầu tiên mà nhiều người gặp phải với sharding là độ phức tạp trong việc triển khai đúng kiến trúc cơ sở dữ liệu dạng sharded. Nếu thực hiện sai, quá trình sharding có thể dẫn đến mất dữ liệu hoặc làm hỏng bảng. Ngay cả khi thực hiện đúng, sharding vẫn có thể ảnh hưởng lớn đến quy trình làm việc của nhóm phát triể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 xử lý dữ liệu từ nhiều vị trí shard khác nhau, điều này có thể gây gián đoạn cho một số nhóm.
Một vấn đề khác mà người dùng có thể gặp sau khi sharding cơ sở dữ liệu là các shard có thể trở nên mất cân bằng. Ví dụ, giả sử bạn có cơ sở dữ liệu gồm hai shard riêng biệt, một dành cho khách hàng có họ bắt đầu từ A đến M và một dành cho những người có họ từ N đến Z. Tuy nhiên, nếu ứng dụng của bạn phục vụ phần lớn người có họ bắt đầu bằng chữ G, thì shard A–M sẽ dần chứa nhiều dữ liệu hơn shard N–Z, khiến ứng dụng bị chậm hoặc ngừng phản hồi đối với phần lớn người dùng. Shard A–M trong trường hợp này trở thành một “hotspot” của cơ sở dữ liệu. Khi đó, lợi ích của sharding sẽ bị triệt tiêu bởi tình trạng chậm trễ và sập hệ thống. Cơ sở dữ liệu có thể cần được sửa chữa và chia lại shard để dữ liệu được phân phối đồng đều hơn.
Một nhược điểm lớn khác là khi cơ sở dữ liệu đã được sharding, việc khôi phục lại kiến trúc ban đầu (không sharded) là rất khó. Các bản sao lưu trước khi sharding sẽ không bao gồm dữ liệu được ghi sau khi phân mảnh. Do đó, việc khôi phục kiến trúc không sharded sẽ yêu cầu gộp dữ liệu mới từ các partition vào bản sao lưu cũ hoặc chuyển đổi toàn bộ cơ sở dữ liệu phân mảnh thành một cơ sở dữ liệu đơn nhất. -Cả hai đều tốn kém thời gian và chi phí.
Một hạn chế cuối cùng cần cân nhắc là không phải hệ quản trị cơ sở dữ liệu nào cũng hỗ trợ sharding một cách tự động (native support). Ví dụ, PostgreSQL không có tính năng sharding tự động , mặc dù vẫn có thể thực hiện sharding thủ công. Có một số phiên bản “fork” của PostgreSQL hỗ trợ sharding tự động, nhưng thường thì chúng chậm cập nhật so với bản chính thức và thiếu một số tính năng khác. Một số công nghệ cơ sở dữ liệu chuyên dụng như MySQL Cluster hoặc các dịch vụ cơ sở dữ liệu như MongoDB Atlas có hỗ trợ auto-sharding, nhưng các phiên bản gốc thì không. Vì lý do này, sharding thường đòi hỏi bạn phải tự triển khai “roll your own”. Điều này cũng đồng nghĩa với việc tài liệu hướng dẫn hoặc mẹo xử lý lỗi về sharding thường rất khó tìm.
Tất nhiên, đây chỉ là một số vấn đề chung cần cân nhắc trước khi áp dụng sharding. Tùy thuộc vào trường hợp sử dụng cụ thể, có thể còn nhiều nhược điểm tiềm ẩn khác.
Bây giờ, sau khi đã tìm hiểu về một số lợi ích và hạn chế của sharding, chúng ta sẽ cùng xem xét một vài kiến trúc sharding phổ biến.
Kiến trúc Sharding
Khi bạn đã quyết định áp dụng sharding cho cơ sở dữ liệu của mình, bước tiếp theo là xác định cách thức triển khai sharding. Khi thực hiện truy vấn hoặc phân phối dữ liệu mới đến các bảng hoặc cơ sở dữ liệu đã được shard, điều quan trọng là dữ liệu phải được gửi đến đúng shard. Nếu không, có thể dẫn đến mất dữ liệu hoặc truy vấn chậm một cách nghiêm trọng. Trong phần này, chúng ta sẽ tìm hiể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 pháp khác nhau để phân phối dữ liệu giữa các shard.
Key Based Sharding
Key based sharding, còn được gọi là hash based sharding, sử dụng một giá trị được lấy từ dữ liệu mới ghi (chẳng hạn như ID của khách hàng, địa chỉ IP của ứng dụng khách, mã ZIP, v.v.) và đưa nó vào một hash function (hàm băm) để xác định shard mà dữ liệu nên được lưu vào.
Hash function là một hàm nhận một đoạn dữ liệu làm đầu vào (ví dụ: email của khách hàng) và trả về một giá trị rời rạc, gọi là hash value. Trong trường hợp sharding, giá trị hash chính là ID của shard – giúp xác định dữ liệu sẽ được lưu ở shard nào. Toàn bộ quá trình này có thể được mô tả như sau:
Để đảm bảo rằng các bản ghi được đặt vào đúng shard một cách nhất quán, các giá trị đưa vào hash function cần phải đến từ cùng một cột. Cột này được gọi là shard key. Nói một cách đơn giản, shard key giống với primary key ở chỗ cả hai đều là những cột được sử dụng để xác định duy nhất một hàng dữ liệu. Về tổng thể, shard key nên là một giá trị tĩnh, tức là không nên chứa các giá trị có thể thay đổi theo thời gian. Nếu không, nó sẽ làm tăng khối lượng công việc khi thực hiện các thao tác cập nhật (update), từ đó làm giảm hiệu suất hệ thống.
Mặc dù key based sharding (sharding dựa trên khóa) là một kiến trúc khá phổ biến, nhưng nó có thể gây ra khó khăn khi bạn muốn thêm hoặc xóa server một cách linh hoạt khỏi cơ sở dữ liệu. Khi thêm máy chủ, mỗi server mới sẽ cần một giá trị hash tương ứng, và nhiều bản ghi hiện có, thậm chí có thể là toàn bộ, sẽ phải được ánh xạ lại với giá trị hash mới và di chuyển sang server phù hợp. Trong quá trình cân bằng lại dữ liệu, cả hash function cũ lẫn mới đều không thể sử dụng được đầy đủ, dẫn đến việc server không thể ghi dữ liệu mới trong lúc di chuyển, và ứng dụng của bạn có thể bị gián đoạn hoạt động.
Lợi thế chính của chiến lược này là khả năng phân phối dữ liệu đồng đều, giúp tránh tình trạng “hotspot” (nơi một shard bị quá tải). Ngoài ra, vì dữ liệu được phân phối theo thuật toán, nên không cần phải duy trì bảng ánh xạ (map) vị trí dữ liệu như các phương pháp khác, ví dụ như range based (sharding theo phạm vi) hoặc directory based sharding (sharding theo danh mục).
Range Based Sharding
Range Based Sharding liên quan đến việc phân chia dữ liệu dựa trên các phạm vi của một giá trị nhất định. Để minh họa, 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 một vài shard khác nhau và chia thông tin của mỗi sản phẩm dựa trên phạm vi giá mà chúng thuộc vào, như sau:
Lợi ích chính của range based sharding là nó tương đối dễ 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 cấu trúc như nhau và giống với cơ sở dữ liệu gốc. Mã của ứng dụng sẽ đọc phạm vi mà dữ liệu thuộc vào và ghi dữ liệu vào shard tương ứng.
Mặt khác, range based sharding không ngăn được việc dữ liệu bị phân bố không đồng đều, dẫn đến tình trạng hotspot như đã đề cập trước đó. Dựa trên sơ đồ ví dụ, ngay cả khi mỗi shard chứa một lượng dữ liệu bằng nhau, thì vẫn có khả năng một số sản phẩm nhất định nhận được sự quan tâm nhiều hơn các sản phẩm khác. Khi đó, các shard tương ứng sẽ phải xử lý số lượng truy vấn đọc không cân đối.
Directory based sharding
Để triển khai directory based sharding, bạn cần tạo và duy trì một lookup table (bảng tra cứu) sử dụng shard key để theo dõi dữ liệu nào được lưu ở shard nào. Lookup table là một bảng chứa một tập thông tin tĩnh về vị trí dữ liệu cụ thể có thể được tìm thấy.
Sơ đồ dưới đây minh họa một ví dụ đơn giản về directory based sharding:
Ở đây, cột Delivery Zone được định nghĩa là một shard key. Dữ liệu từ shard key được ghi vào lookup table cùng với shard tương ứng mà mỗi dòng dữ liệu sẽ được ghi vào. Điều này giống với range based sharding, nhưng thay vì xác định khoảng giá trị mà dữ liệu của shard key thuộc vào, thì mỗi key được gắn trực tiếp với một shard cụ thể. Directory based sharding là một lựa chọn phù hợp hơn so với range based sharding trong các trường hợp mà shard key có độ phân biệt thấp. Nghĩa là chỉ có một số lượng giá trị giới hạn và không hợp lý khi một shard lưu trữ cả một khoảng các key. Lưu ý rằng nó cũng khác với key based sharding ở chỗ nó không xử lý shard key thông qua một hash function, thay vào đó, nó chỉ đối chiếu key với lookup table để xác định nơi cần ghi dữ liệu.
Điểm hấp dẫn chính của directory based sharding là tính linh hoạt của nó. Range based sharding giới hạn bạn ở việc xác định các khoảng giá trị, trong khi key based sharding giới hạn bạn ở việc sử dụng một hash function cố định, và như đã đề cập trước đó, điều này có thể cực kỳ khó thay đổi về sau. Ngược lại, directory based sharding cho phép bạn sử dụng bất kỳ hệ thống hoặc thuật toán nào để phân phối các bản ghi dữ liệu vào các shard, và cách tiếp cận này cũng tương đối dễ dàng để thêm các shard mới một cách linh hoạt.
Mặc dù directory based sharding là phương pháp linh hoạt nhất trong số các phương pháp sharding được thảo luận ở đây, nhu cầu phải truy cập vào lookup table 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, lookup table có thể trở thành một điểm lỗi đơn lẻ: nếu nó bị hỏng hoặc gặp sự cố, điều đó có thể ảnh hưởng đến khả năng ghi dữ liệu mới hoặc truy cập dữ liệu hiện có của bạn.
Tôi có nên sử dụng Sharding không?
Việc có nên triển khai kiến trúc cơ sở dữ liệu dạng sharded hay không gần như luôn là một vấn đề gây tranh luận. Một số người cho rằng 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 những người khác lại xem nó như một rắc rối cần tránh, trừ khi thực sự cần thiết, do độ phức tạp vận hành mà nó mang lại.
Chính vì sự phức tạp đó, sharding thường chỉ được áp dụng khi xử lý lượng dữ liệu rất lớn. Dưới đây là một số kịch bản phổ biến trong đó việc sharding có thể mang lại lợi ích:
- Lượng dữ liệu của ứng dụng tăng đến mức vượt quá dung lượng lưu trữ của một node cơ sở dữ liệu đơn lẻ.
- Khối lượng ghi hoặc đọc từ cơ sở dữ liệu vượt quá khả năng xử lý của một node hoặc các bản sao đọc của nó, dẫn đến thời gian phản hồi chậm hoặc lỗi timeout.
- Băng thông mạng cần thiết cho ứng dụng lớn hơn băng thông mà một node cơ sở dữ liệu (và các bản sao đọc) có thể đáp ứng, dẫn đến hiện tượng tương tự như trên.
Trước khi quyết định sử dụng sharding, bạn nên kiểm tra và áp dụng tất cả các phương án tối ưu hóa khác cho cơ sở dữ liệu. Một số phương pháp tối ưu bạn có thể cân nhắc bao gồm:
- Cấu hình cơ sở dữ liệu từ xa: Nếu bạn đang làm việc với một ứng dụng đơn khối trong đó mọi thành phần đều nằm trên cùng một máy chủ, bạn có thể cải thiện hiệu năng cơ sở dữ liệu bằng cách chuyển nó sang một máy riêng. Cách làm này ít phức tạp hơn sharding vì không thay đổi cấu trúc bảng dữ liệu, nhưng vẫn cho phép vertical scaling cơ sở dữ liệu độc lập với hạ tầng còn lại.
- Triển khai caching: Nếu vấn đề nằm ở hiệu năng đọc, bộ nhớ đệm (caching) là một giải pháp hữu ích. Caching là quá trình lưu tạm thời dữ liệu đã được truy vấn vào bộ nhớ, giúp truy cập nhanh hơn ở những lần sau.
- Tạo bản sao đọc (read replicas): Một cách khác để cải thiện hiệu suất đọc là sao chép dữ liệu từ máy chủ cơ sở dữ liệu chính sang một hoặc nhiều máy chủ phụ. Mọi ghi mới sẽ được thực hiện trên máy chủ chính trước khi sao chép sang máy chủ phụ, trong khi các truy vấn đọc chỉ thực hiện trên máy chủ phụ. Việc tách biệt đọc và ghi như vậy giúp tránh quá tải cho bất kỳ máy nào, giảm nguy cơ chậm hoặc sập hệ thống. Lưu ý rằng tạo read replica cần nhiều tài nguyên tính toán hơn và do đó tốn chi phí cao hơn, điều này có thể là rào cản với một số tổ chức.
- Nâng cấp lên máy chủ lớn hơn: Trong hầu hết trường hợp, scale up bằng cách nâng cấp phần cứng của máy chủ dễ hơn nhiều so với triển khai sharding. Tuy nhiên, cũng như việc tạo bản sao đọc, việc nâng cấp phần cứng sẽ tăng chi phí, nên chỉ nên thực hiện nếu nó thực sự là lựa chọn tốt nhất.
Hãy nhớ rằng nếu ứng dụng hoặc website của bạn phát triển vượt qua một ngưỡng nhất định, không có chiến lược nào ở trên sẽ đủ để cải thiện hiệu năng một cách riêng lẻ. Trong những trường hợp như vậy, sharding có thể thực sự là giải pháp phù hợp nhất dành cho bạn.
Kết luận
Sharding có thể là một giải pháp tuyệt vời cho những ai đang tìm cách mở rộng cơ sở dữ liệu theo phương pháp horizontal scaling. Tuy nhiên, nó cũng đi kèm với độ phức tạp cao và làm tăng khả năng xảy ra lỗi trong ứng dụng của bạn. Với một số trường hợp, sharding là điều cần thiết, nhưng đối với những trường hợp khác, thời gian và tài nguyên cần thiết để xây dựng và duy trì kiến trúc sharded có thể vượt quá lợi ích mà nó mang lại.
Thông qua bài viết mang tính khái niệm này, bạn sẽ có cái nhìn rõ ràng hơn về ưu và nhược điểm của sharding. Từ đó, bạn có thể đưa ra quyết định sáng suốt hơn về việc liệu kiến trúc cơ sở dữ liệu dạng sharded có phù hợp với ứng dụng của bạn hay không.