- Tóm lược hành trình chín tháng của đội ngũ cơ sở dữ liệu Figma trong việc phân mảnh ngang stack Postgres và cách họ tạo ra khả năng mở rộng gần như vô hạn
Hành trình phân mảnh ngang stack Postgres của Figma
- Quy mô stack cơ sở dữ liệu của Figma đã tăng gần 100 lần kể từ năm 2020: Đây là một vấn đề tích cực vì phản ánh sự mở rộng kinh doanh, nhưng đồng thời cũng tạo ra thách thức kỹ thuật. Vào năm 2020, họ vận hành một cơ sở dữ liệu Postgres duy nhất trên instance vật lý lớn nhất của AWS, và đến cuối năm 2022 đã xây dựng một kiến trúc phân tán bao gồm cache, read replica và nhiều cơ sở dữ liệu phân vùng dọc.
- Phân vùng dọc: Tách các nhóm bảng liên quan thành những phân vùng dọc riêng để đạt được lợi ích mở rộng dần theo từng bước và duy trì đủ dư địa để đón đầu tăng trưởng. Ví dụ, họ chia các nhóm bảng liên quan như “tệp Figma” hoặc “tổ chức” thành các phân vùng dọc riêng.
- Chuyển sang phân mảnh ngang: Họ nhận ra chỉ phân vùng dọc thôi là chưa đủ. Sau các nỗ lực mở rộng ban đầu tập trung vào giảm mức sử dụng CPU, họ bắt đầu theo dõi nhiều nút thắt khác nhau trên một fleet lớn hơn và đa dạng hơn. Họ định lượng các giới hạn mở rộng cơ sở dữ liệu trên nhiều phương diện, từ CPU và IO đến kích thước bảng và số hàng được ghi. Việc xác định các giới hạn này rất quan trọng để dự đoán còn bao nhiêu dư địa trên mỗi shard.
- Giới hạn của kích thước bảng: Một số bảng đã đạt đến kích thước khó có thể xử lý bằng một cơ sở dữ liệu đơn lẻ, với nhiều terabyte dữ liệu và hàng tỷ hàng. Ở quy mô này, các tác vụ vacuum của Postgres (công việc nền thiết yếu để tránh hệ thống dừng vì cạn transaction ID) bắt đầu ảnh hưởng đến độ tin cậy. Những bảng có lượng ghi cao nhất sớm sẽ vượt quá mức IOPS tối đa mà Amazon RDS hỗ trợ. Đây là vấn đề mà phân vùng dọc không thể giải quyết, và cần một giải pháp lớn hơn để ngăn cơ sở dữ liệu sụp đổ.
Xây nền cho việc mở rộng quy mô
- Giảm thiểu tác động đến lập trình viên: Họ xử lý phần lớn mô hình dữ liệu quan hệ phức tạp để các lập trình viên ứng dụng có thể tập trung xây dựng các tính năng mới thú vị tại Figma, thay vì phải refactor những phần lớn của codebase.
- Mở rộng minh bạch: Họ muốn bảo đảm rằng trong tương lai có thể mở rộng mà không cần thêm thay đổi ở tầng ứng dụng. Nói cách khác, sau phần chuẩn bị ban đầu để làm cho các bảng tương thích, các lần mở rộng sau này có thể diễn ra minh bạch với các nhóm sản phẩm.
- Tránh backfill tốn kém: Họ tránh những giải pháp đòi hỏi backfill cho các bảng lớn hoặc toàn bộ bảng của Figma. Với kích thước bảng và giới hạn thông lượng của Postgres, các backfill như vậy sẽ mất nhiều tháng.
- Triển khai từng bước: Họ xác định một cách tiếp cận có thể được phát hành dần dần để giảm rủi ro của các thay đổi lớn trong môi trường production. Điều này làm giảm nguy cơ gián đoạn nghiêm trọng và giúp đội ngũ cơ sở dữ liệu duy trì độ tin cậy của Figma trong suốt quá trình migration.
- Tránh migration một chiều: Họ duy trì khả năng rollback ngay cả sau khi hoàn tất phân mảnh vật lý. Điều này làm giảm rủi ro bị mắc kẹt trong trạng thái xấu khi gặp các biến số chưa biết.
- Duy trì tính nhất quán dữ liệu mạnh: Họ tránh các giải pháp phức tạp khó triển khai mà không downtime hoặc làm tổn hại tính nhất quán, chẳng hạn như double-writes. Họ muốn một giải pháp có thể mở rộng với gần như không downtime.
- Tận dụng thế mạnh của mình: Trong khi làm việc dưới áp lực deadline gắt gao, họ ưu tiên cách tiếp cận có thể phát hành dần dần nhiều nhất có thể. Với các bảng tăng trưởng nhanh nhất, họ cố gắng tận dụng chuyên môn và công nghệ sẵn có.
Khám phá các lựa chọn khả thi
- Xem xét các lựa chọn cơ sở dữ liệu phân mảnh ngang: Có nhiều giải pháp mã nguồn mở và managed phổ biến dành cho cơ sở dữ liệu phân mảnh ngang tương thích với Postgres hoặc MySQL. Họ đã xem xét CockroachDB, TiDB, Spanner và Vitess trong quá trình đánh giá. Tuy nhiên, việc chuyển sang các cơ sở dữ liệu thay thế này sẽ đòi hỏi migration dữ liệu phức tạp để bảo đảm tính nhất quán và độ tin cậy giữa hai kho dữ liệu khác nhau.
- Tận dụng chuyên môn hiện có: Trong vài năm gần đây, họ đã phát triển rất nhiều chuyên môn về cách vận hành RDS Postgres một cách ổn định và hiệu quả. Nếu migration, họ sẽ phải xây dựng lại chuyên môn miền từ đầu. Với tốc độ tăng trưởng cực kỳ cao, thời gian còn lại chỉ tính bằng vài tháng.
- Loại trừ lựa chọn cơ sở dữ liệu NoSQL: Một giải pháp mở rộng phổ biến khác mà các công ty thường chọn khi phát triển là cơ sở dữ liệu NoSQL. Tuy nhiên, họ đang có một mô hình dữ liệu quan hệ rất phức tạp được xây dựng trên kiến trúc Postgres hiện tại, và API NoSQL không cung cấp mức độ đa dạng đó. Họ muốn các kỹ sư tập trung phát hành những tính năng tuyệt vời và xây dựng sản phẩm mới thay vì viết lại gần như toàn bộ ứng dụng backend; vì thế NoSQL không phải là giải pháp khả thi.
- Cân nhắc xây dựng giải pháp phân mảnh ngang trên hạ tầng RDS Postgres hiện có: Việc một đội nhỏ tự tái triển khai nội bộ một cơ sở dữ liệu quan hệ phân mảnh ngang đa dụng là không hợp lý. Làm vậy sẽ đồng nghĩa cạnh tranh với các công cụ do cộng đồng mã nguồn mở lớn hoặc các nhà cung cấp cơ sở dữ liệu chuyên nghiệp xây dựng. Tuy nhiên, vì họ tùy biến phân mảnh ngang cho kiến trúc cụ thể của Figma, chỉ cần cung cấp một tập tính năng nhỏ hơn nhiều là đủ. Ví dụ, họ quyết định không hỗ trợ transaction liên shard có bảo đảm tính nguyên tử, vì họ có cách xử lý lỗi transaction liên shard. Họ chọn chiến lược colocation để giảm thiểu các thay đổi cần thiết ở tầng ứng dụng. Nhờ đó, họ có thể hỗ trợ một tập con của Postgres tương thích với phần lớn logic sản phẩm. Đồng thời, họ cũng có thể dễ dàng duy trì khả năng tương thích ngược giữa Postgres đã phân mảnh và Postgres chưa phân mảnh. Nếu đối mặt với các biến số chưa biết, họ có thể dễ dàng rollback về Postgres chưa phân mảnh.
Con đường đến với phân mảnh ngang
- Triển khai phân mảnh ngang: Phân mảnh ngang là quá trình chia nhỏ một bảng đơn lẻ hoặc một nhóm bảng để phân phối dữ liệu trên nhiều instance cơ sở dữ liệu vật lý. Thông qua quá trình này, ở tầng ứng dụng, các bảng được phân mảnh ngang có thể hỗ trợ bất kỳ số lượng shard nào ở tầng vật lý. Họ luôn có thể mở rộng thêm chỉ bằng cách thực hiện tách shard vật lý, và các thao tác này diễn ra minh bạch ở chế độ nền với downtime tối thiểu và không cần thay đổi ở cấp độ ứng dụng. Nhờ khả năng này, Figma có thể vượt trước các nút thắt mở rộng cơ sở dữ liệu còn lại và loại bỏ một trong những thách thức mở rộng quy mô lớn cuối cùng của mình. Nếu phân vùng dọc giúp họ tăng tốc như trên xa lộ, thì phân mảnh ngang là gỡ bỏ giới hạn tốc độ để có thể bay.
- Độ phức tạp của phân mảnh ngang: Phân mảnh ngang phức tạp hơn một bậc so với các nỗ lực mở rộng trước đó. Khi bảng được chia trên nhiều cơ sở dữ liệu vật lý, nhiều thuộc tính về độ tin cậy và tính nhất quán vốn được xem là hiển nhiên trong cơ sở dữ liệu SQL tuân thủ ACID sẽ bị mất đi. Ví dụ, một số truy vấn SQL có thể trở nên kém hiệu quả hoặc không thể hỗ trợ, và mã ứng dụng cần được cập nhật để cung cấp đủ thông tin nhằm định tuyến truy vấn đến đúng shard một cách hiệu quả nhất có thể. Các thay đổi schema phải được điều phối để mọi shard luôn đồng bộ, còn foreign key và chỉ mục duy nhất toàn cục sẽ không còn được Postgres thực thi nữa. Transaction nay có thể trải rộng qua nhiều shard, khiến Postgres không còn khả năng ép buộc transaction đó. Khi đó, một số thao tác ghi vào cơ sở dữ liệu có thể thành công trong khi các thao tác khác thất bại. Logic sản phẩm phải được thiết kế cẩn thận để đủ vững trước các “lỗi commit một phần” như vậy (ví dụ, hãy tưởng tượng việc chuyển một team giữa hai tổ chức mà chỉ một nửa dữ liệu bị thiếu!).
- Nỗ lực nhiều năm hướng tới phân mảnh ngang: Họ biết rằng để đạt được phân mảnh ngang hoàn chỉnh sẽ là một nỗ lực kéo dài nhiều năm. Họ cần mang lại giá trị gia tăng dần dần đồng thời giảm rủi ro dự án nhiều nhất có thể. Mục tiêu đầu tiên là phân mảnh một bảng tương đối đơn giản nhưng có lưu lượng rất cao trong môi trường production càng sớm càng tốt. Điều này không chỉ chứng minh tính khả thi của phân mảnh ngang mà còn giúp kéo dài dư địa cho cơ sở dữ liệu đang chịu tải lớn nhất. Sau đó, họ có thể xây dựng thêm tính năng trong lúc phân mảnh các nhóm bảng phức tạp hơn. Ngay cả bộ tính năng đơn giản nhất có thể cũng vẫn là một khối lượng công việc đáng kể. Từ đầu đến cuối, đội ngũ của họ mất khoảng chín tháng để phân mảnh bảng đầu tiên.
Cách tiếp cận độc đáo của chúng tôi
- Colocation (Colos): Shard ngang các nhóm bảng liên quan thành các colocation (được gọi thân mật là “colo”) dùng chung cùng một khóa sharding và bố cục sharding vật lý. Điều này cung cấp cho lập trình viên một lớp trừu tượng quen thuộc để tương tác với các bảng đã được shard ngang.
- Sharding logic: Tách khái niệm “sharding logic” ở lớp ứng dụng khỏi “sharding vật lý” ở lớp Postgres. Sử dụng view để triển khai sharding logic an toàn hơn và ít tốn kém hơn trước khi thực hiện chuyển đổi dự phòng vật lý phân tán có rủi ro cao hơn.
- Bộ máy truy vấn DBProxy: Xây dựng dịch vụ DBProxy để chặn các truy vấn SQL được tạo ở lớp ứng dụng và định tuyến truy vấn động đến nhiều cơ sở dữ liệu Postgres khác nhau. DBProxy bao gồm một bộ máy truy vấn có thể phân tích và thực thi các truy vấn sharding ngang phức tạp. Thông qua DBProxy, họ có thể triển khai các tính năng như cân bằng tải động và hedging request.
- Mức độ sẵn sàng ứng dụng bóng: Bổ sung một framework “mức độ sẵn sàng ứng dụng bóng” có thể dự đoán lưu lượng production thực tế sẽ hoạt động ra sao dưới nhiều khóa sharding tiềm năng khác nhau. Điều này cung cấp cho các nhóm sản phẩm cái nhìn rõ ràng về việc cần refactor hoặc loại bỏ logic ứng dụng nào để chuẩn bị cho sharding ngang.
- Sao chép logic toàn phần: Không cần triển khai “sao chép logic có lọc”, tức chỉ sao chép một tập con dữ liệu đến từng shard. Thay vào đó, họ sao chép toàn bộ tập dữ liệu, rồi chỉ cho phép đọc/ghi trên phần dữ liệu thuộc về shard tương ứng.
Triển khai sharding
- Tầm quan trọng của việc chọn shard key: Một trong những quyết định quan trọng nhất trong sharding ngang là sẽ dùng shard key nào. Sharding ngang áp thêm nhiều ràng buộc mô hình dữ liệu xoay quanh shard key. Ví dụ, phần lớn truy vấn phải bao gồm shard key để có thể định tuyến yêu cầu đến đúng shard. Một số ràng buộc cơ sở dữ liệu nhất định, chẳng hạn khóa ngoại, chỉ hoạt động khi khóa ngoại cũng là khóa sharding. Shard key cũng phải phân phối dữ liệu đồng đều trên tất cả các shard để tránh các hotspot có thể gây ra vấn đề về độ tin cậy hoặc ảnh hưởng đến khả năng mở rộng.
- Cách tiếp cận tùy biến theo mô hình dữ liệu của Figma: Figma hoạt động trong trình duyệt và nhiều người dùng có thể cộng tác trên cùng một tệp Figma cùng lúc. Điều này có nghĩa là nó được vận hành bởi một mô hình dữ liệu quan hệ tương đối phức tạp, ghi nhận metadata tệp, metadata tổ chức, bình luận, phiên bản tệp, v.v. Vì không có một ứng viên phù hợp duy nhất trong mô hình dữ liệu hiện có, họ từng cân nhắc dùng cùng một shard key cho mọi bảng, nhưng điều đó sẽ đòi hỏi tạo khóa tổng hợp để thêm một shard key thống nhất, thêm cột vào schema của mọi bảng, chạy backfill tốn kém để điền dữ liệu, rồi refactor đáng kể logic sản phẩm. Thay vào đó, họ tùy biến cách tiếp cận cho phù hợp với mô hình dữ liệu đặc thù của Figma bằng cách chọn một số ít shard key như UserID, FileID và OrgID. Gần như mọi bảng của Figma đều có thể được shard bằng một trong các khóa này.
- Đưa vào Colocation (Colos): Họ đưa vào khái niệm colocation để cung cấp một lớp trừu tượng thân thiện cho lập trình viên sản phẩm. Các bảng trong cùng một colo hỗ trợ join liên bảng và giao dịch đầy đủ khi bị giới hạn trong một shard key duy nhất. Phần lớn mã ứng dụng vốn đã tương tác với cơ sở dữ liệu theo cách này, nên giảm thiểu công việc mà lập trình viên ứng dụng phải làm để điều chỉnh bảng cho phù hợp với sharding ngang.
- Đảm bảo tính đồng đều của phân phối dữ liệu: Sau khi chọn shard key, cần đảm bảo dữ liệu được phân phối đồng đều trên toàn bộ các cơ sở dữ liệu backend. Đáng tiếc là nhiều shard key được chọn sử dụng ID tự tăng hoặc ID có tiền tố timestamp Snowflake. Điều này lẽ ra sẽ tạo ra các hotspot đáng kể, nơi phần lớn dữ liệu tập trung vào một shard duy nhất. Họ đã xem xét việc chuyển sang các ID ngẫu nhiên hơn, nhưng điều đó đòi hỏi một đợt di chuyển dữ liệu tốn kém và mất thời gian. Thay vào đó, họ quyết định dùng hàm băm của shard key để định tuyến. Nếu chọn một hàm băm đủ ngẫu nhiên, có thể đảm bảo dữ liệu được phân phối đồng đều. Một nhược điểm của cách này là quét theo khoảng trên shard key trở nên kém hiệu quả hơn, vì các khóa liên tiếp sẽ bị băm sang các shard cơ sở dữ liệu khác nhau. Tuy vậy, vì kiểu truy vấn này không phổ biến trong codebase của họ, đây là một sự đánh đổi có thể chấp nhận.
Giải pháp “logic”
- Giảm rủi ro khi triển khai sharding ngang: Để giảm rủi ro khi triển khai sharding ngang, họ muốn tách quá trình vật lý thực hiện chia shard khỏi quá trình chuẩn bị bảng ở lớp ứng dụng. Vì vậy, họ tách “sharding logic” và “sharding vật lý”. Nhờ đó, hai phần của quá trình migration có thể được triển khai độc lập để giảm rủi ro. Sharding logic mang lại sự tin cậy vào stack phục vụ thông qua triển khai theo tỷ lệ với rủi ro thấp. Khi phát hiện lỗi, việc rollback sharding logic chỉ là một thay đổi cấu hình đơn giản. Việc rollback công việc shard vật lý tuy có thể thực hiện được, nhưng cần phối hợp phức tạp hơn để đảm bảo tính nhất quán dữ liệu.
- Hành vi sau sharding logic: Khi một bảng đã được shard về mặt logic, mọi thao tác đọc và ghi đều hoạt động như thể nó đã được shard ngang. Về độ tin cậy, độ trễ và tính nhất quán, hệ thống trông như đã shard ngang, nhưng dữ liệu trên thực tế vẫn nằm vật lý trên một máy chủ cơ sở dữ liệu duy nhất. Khi đã tin tưởng rằng sharding logic hoạt động đúng như mong đợi, họ mới thực hiện công việc sharding vật lý. Quá trình này bao gồm sao chép dữ liệu từ một cơ sở dữ liệu đơn sang nhiều backend đã được shard, rồi định tuyến lại lưu lượng đọc và ghi qua các cơ sở dữ liệu mới.
Bộ máy truy vấn có thể làm được việc
- Thiết kế lại backend stack để hỗ trợ sharding ngang: Ban đầu, dịch vụ ứng dụng giao tiếp trực tiếp với PGBouncer, lớp connection pooling. Tuy nhiên, sharding ngang đòi hỏi việc phân tích, lập kế hoạch và thực thi truy vấn phức tạp hơn nhiều. Để hỗ trợ điều này, họ xây dựng DBProxy, một dịch vụ Golang mới. DBProxy nằm giữa lớp ứng dụng và PGBouncer. Nó chứa logic cho cân bằng tải, khả năng quan sát tốt hơn, hỗ trợ giao dịch, quản lý topology cơ sở dữ liệu và một bộ máy truy vấn nhẹ.
- Các thành phần cốt lõi của bộ máy truy vấn:
- Trình phân tích truy vấn: Đọc SQL được gửi từ ứng dụng và chuyển nó thành cây cú pháp trừu tượng (AST).
- Bộ lập kế hoạch logic: Phân tích AST và trích xuất loại truy vấn (insert, update, v.v.) cùng ID shard logic từ kế hoạch truy vấn.
- Bộ lập kế hoạch vật lý: Ánh xạ truy vấn từ ID shard logic sang cơ sở dữ liệu vật lý. Viết lại truy vấn để thực thi trên shard vật lý phù hợp.
- Phương thức “scatter-gather”: Hoạt động như một trò trốn tìm trên toàn bộ cơ sở dữ liệu: gửi truy vấn đến mọi shard (phân tán), rồi thu thập câu trả lời từ từng shard (gom lại). Nghe thú vị, nhưng nếu lạm dụng với truy vấn phức tạp, cơ sở dữ liệu có thể chậm như sên.
- Triển khai truy vấn trong thế giới sharding ngang: Truy vấn single-shard được lọc bằng một shard key duy nhất. Bộ máy truy vấn chỉ cần trích xuất shard key và định tuyến truy vấn đến đúng cơ sở dữ liệu vật lý. Nó “đẩy” độ phức tạp của việc thực thi truy vấn xuống cho Postgres. Tuy nhiên, nếu truy vấn thiếu shard key, bộ máy truy vấn phải thực hiện quy trình “scatter-gather” phức tạp hơn. Trong trường hợp này, truy vấn phải được fan-out đến mọi shard (giai đoạn phân tán), sau đó kết quả được tổng hợp lại (giai đoạn thu thập).
- Đơn giản hóa khả năng tương thích SQL: Nếu dịch vụ DBProxy hỗ trợ tương thích SQL đầy đủ, nó sẽ trông rất giống với bộ máy truy vấn của cơ sở dữ liệu Postgres. Họ muốn đơn giản hóa API để giảm thiểu độ phức tạp của DBProxy và giảm bớt công việc cho lập trình viên ứng dụng, những người sẽ phải viết lại các truy vấn không được hỗ trợ. Để xác định tập con phù hợp, họ xây dựng một framework “shadow planning” có thể định nghĩa schema sharding tiềm năng của các bảng và chạy bước lập kế hoạch logic trên lưu lượng production thời gian thực. Họ có thể ghi log truy vấn và kế hoạch truy vấn liên quan vào cơ sở dữ liệu Snowflake để phân tích offline. Từ dữ liệu này, họ chọn một ngôn ngữ truy vấn hỗ trợ 90% truy vấn phổ biến nhất đồng thời tránh độ phức tạp ở trường hợp xấu nhất của bộ máy truy vấn. Ví dụ, mọi truy vấn quét theo khoảng và point query đều được cho phép, nhưng join chỉ được cho phép giữa hai bảng trong cùng một colo khi được thực hiện trên shard key.
Triển vọng tương lai
- Đóng gói shard logic: Cần quyết định cách đóng gói shard logic. Nhóm đã khảo sát việc phân chia dữ liệu bằng cơ sở dữ liệu Postgres riêng biệt hoặc schema Postgres. Đáng tiếc là cách này đòi hỏi thay đổi dữ liệu vật lý khi shard theo logic, và mức độ phức tạp tương đương với việc chia shard vật lý.
- Biểu diễn shard bằng view Postgres: Thay vào đó, nhóm quyết định biểu diễn shard bằng view Postgres. Mỗi bảng có thể có nhiều view, mỗi view tương ứng với một tập con dữ liệu của shard nhất định. Cách này có dạng như sau:
CREATE VIEW table_shard1 AS SELECT * FROM table WHERE hash(shard_key) >= min_shard_range AND hash(shard_key) < max_shard_range). Mọi thao tác đọc và ghi đều đi qua các view này.
- Tạo view đã shard trên cơ sở dữ liệu vật lý chưa shard hiện có: Có thể shard theo logic trước khi thực hiện thao tác reshard vật lý đầy rủi ro. Mỗi view được truy cập thông qua dịch vụ connection pooler đã shard riêng của nó. Bằng cách để connection pooler vẫn trỏ tới instance vật lý chưa shard, hệ thống có thể hoạt động như thể đã được shard. Nhờ feature flag trong query engine, việc phát hành đọc/ghi đã shard có thể được triển khai dần để giảm rủi ro, đồng thời có thể rollback bất cứ lúc nào chỉ trong vài giây bằng cách định tuyến lại lưu lượng về bảng chính. Trước khi chạy đợt reshard đầu tiên, nhóm đã có thể tự tin vào độ an toàn của topology đã shard.
- Rủi ro khi phụ thuộc vào view: View tạo thêm overhead hiệu năng và trong một số trường hợp có thể làm thay đổi căn bản cách Postgres query planner tối ưu hóa truy vấn. Để xác thực cách tiếp cận này, nhóm đã thu thập một tập truy vấn sản xuất đã được làm sạch và chạy kiểm thử tải cả khi có và không có view. Kết quả cho thấy trong hầu hết trường hợp, view chỉ tạo ra overhead hiệu năng tối thiểu, và ngay cả ở trường hợp xấu nhất cũng dưới 10%. Nhóm cũng xây dựng một framework shadow read để gửi toàn bộ lưu lượng đọc thời gian thực qua view và so sánh hiệu năng cũng như độ chính xác giữa truy vấn có view và không có view. Kết quả xác nhận rằng view là một giải pháp khả thi với tác động hiệu năng tối thiểu.
Giải quyết bài toán topology
- DBProxy cần hiểu topology để định tuyến truy vấn: Cần hiểu topology của các bảng và cơ sở dữ liệu vật lý. Việc tách biệt khái niệm shard logic và shard vật lý tạo ra nhu cầu phải có cách biểu diễn các lớp trừu tượng này trong topology.
- Ánh xạ bảng và shard key: Cần có cách ánh xạ bảng
users với shard key user_id, cũng như ánh xạ shard ID logic (123) tới đúng cơ sở dữ liệu logic và vật lý.
- Phân vùng dọc và sự phụ thuộc vào file cấu hình hard-code: Trong phân vùng dọc, nhóm dựa vào các file cấu hình hard-code đơn giản để ánh xạ bảng tới phân vùng tương ứng. Việc chuyển sang horizontal sharding đòi hỏi một hệ thống phức tạp hơn.
- Thay đổi topology động và yêu cầu DBProxy cập nhật trạng thái nhanh: Khi chia shard, topology thay đổi động, vì vậy DBProxy phải cập nhật trạng thái nhanh để tránh định tuyến yêu cầu tới sai cơ sở dữ liệu.
- Tính tương thích ngược của thay đổi topology: Mọi thay đổi topology đều phải tương thích ngược, tránh tạo ra thay đổi nằm trên critical path của site.
- Xây dựng database topology bao bọc metadata horizontal sharding phức tạp: Nhóm đã xây dựng một database topology có thể bao bọc metadata horizontal sharding phức tạp và cung cấp cập nhật gần thời gian thực trong chưa đầy 1 giây.
- Đơn giản hóa quản lý cơ sở dữ liệu bằng cách tách topology logic và vật lý: Việc tách topology logic và vật lý giúp giảm chi phí và độ phức tạp bằng cách giữ nguyên topology logic của môi trường production trong các môi trường không phải production, đồng thời giảm số lượng cơ sở dữ liệu vật lý.
- Thực thi các bất biến topology qua thư viện topology: Duy trì tính chính xác của hệ thống khi xây dựng horizontal sharding bằng cách thực thi các bất biến trong topology, chẳng hạn mọi shard ID phải được ánh xạ tới đúng một cơ sở dữ liệu vật lý.
Công việc shard vật lý
- Bước cuối sau khi bảng đã sẵn sàng cho sharding: Failover vật lý từ cơ sở dữ liệu chưa shard sang cơ sở dữ liệu đã shard. Nhóm có thể tái sử dụng phần lớn logic tương tự cho horizontal sharding, nhưng vẫn có một số khác biệt đáng chú ý như chuyển từ quan hệ cơ sở dữ liệu 1:1 sang 1:N.
- Cần tăng độ bền của quy trình failover: Quy trình failover phải được làm bền vững hơn để ứng phó với các chế độ lỗi mới, trong đó thao tác sharding có thể chỉ thành công trên một phần của cơ sở dữ liệu.
- Phần lớn rủi ro đã được xử lý trong giai đoạn phân vùng dọc: Vì nhiều rủi ro đã được giảm thiểu trong quá trình phân vùng dọc, nhóm có thể tiến tới đợt shard vật lý đầu tiên nhanh hơn nhiều so với mức bình thường.
Vị trí hiện tại trong hành trình horizontal sharding
- Đầu tư nhiều năm cho horizontal sharding: Sau khi nhận ra cần đầu tư nhiều năm vào horizontal sharding cho khả năng mở rộng trong tương lai của Figma, nhóm đã phát hành bảng horizontal sharding đầu tiên vào tháng 9/2023.
- Thực hiện failover thành công: Đạt được failover thành công với 10 giây chỉ khả dụng một phần tạm thời trên database primary và không ảnh hưởng tới khả dụng của replica. Sau khi shard, không có hồi quy nào về độ trễ hay khả dụng.
- Xử lý các shard phức tạp: Nhóm đã xử lý một shard tương đối đơn giản của cơ sở dữ liệu có tốc độ ghi cao nhất. Trong năm nay, họ dự kiến shard các cơ sở dữ liệu ngày càng phức tạp hơn, với hàng chục bảng và hàng nghìn điểm gọi trong mã nguồn.
- Cần horizontal sharding cho mọi bảng tại Figma: Để loại bỏ giới hạn mở rộng cuối cùng và thực sự cất cánh. Một thế giới được horizontal sharding hoàn toàn mang lại nhiều lợi ích như cải thiện độ tin cậy, giảm chi phí và tăng tốc độ phát triển.
- Những vấn đề cần giải quyết:
- Hỗ trợ cập nhật schema đã horizontal sharding
- Tạo ID duy nhất toàn cục cho khóa chính đã horizontal sharding
- Giao dịch liên shard có tính nguyên tử cho các use case cốt lõi của doanh nghiệp
- Chỉ mục phân tán duy nhất toàn cục (hiện chỉ hỗ trợ với các chỉ mục bao gồm shard key)
- Tăng tốc độ phát triển với các model ORM tương thích mượt mà với horizontal sharding
- Tự động hóa hoàn toàn thao tác reshard để có thể chạy chia shard chỉ bằng một cú nhấp nút
- Đánh giá lại cách tiếp cận horizontal sharding hiện tại trên RDS: Hành trình này bắt đầu 18 tháng trước dưới áp lực deadline rất gắt gao. Trong khi NewSQL store tiếp tục phát triển và trưởng thành, nhóm hiện có đủ dư địa để đánh giá lại trade-off giữa việc giữ nguyên lộ trình hiện tại và chuyển sang giải pháp mã nguồn mở hoặc managed solution.
- Tiến triển thú vị trong hành trình horizontal sharding: Các thách thức cần giải quyết vẫn chỉ mới ở giai đoạn đầu. Nhóm kỳ vọng sẽ có thêm nhiều phân tích chuyên sâu hơn về các phần khác nhau của stack horizontal sharding. Nếu bạn quan tâm đến các dự án như thế này, hãy liên hệ. Họ đang tuyển dụng.
Ý kiến của GN⁺
- Đội ngũ cơ sở dữ liệu của Figma đã tìm cách vượt qua giới hạn về khả năng mở rộng cơ sở dữ liệu bằng horizontal sharding, đây là một bước quan trọng để duy trì tăng trưởng và hiệu năng của công cụ cộng tác trên nền tảng đám mây.
- Horizontal sharding đặt ra những thách thức mới trong quản lý dữ liệu và tối ưu hóa truy vấn, đòi hỏi quản trị viên cơ sở dữ liệu và nhà phát triển phải có kiến thức và kỹ năng mới.
- Horizontal sharding cải thiện đáng kể khả năng mở rộng của cơ sở dữ liệu, nhưng cũng đòi hỏi các giải pháp mới cho việc xử lý truy vấn phức tạp và duy trì tính nhất quán dữ liệu.
- Một dự án mã nguồn mở cung cấp chức năng tương tự là CitusDB, cho phép mở rộng cơ sở dữ liệu Postgres theo chiều ngang.
- Khi áp dụng công nghệ horizontal sharding, cần cân nhắc độ phức tạp của mô hình dữ liệu, hiệu năng truy vấn, tính linh hoạt của hệ thống và khía cạnh bảo trì, tức là phải tìm được điểm cân bằng giữa khả năng mở rộng và sự dễ quản lý của cơ sở dữ liệu.
1 bình luận
Ý kiến trên Hacker News
Bảng dung lượng lớn và giới hạn IOPS của RDS
Kết quả sharding và chi phí
Thời gian và chi phí dành cho sharding
So sánh chi phí với YugabyteDB
Đề xuất tách cơ sở dữ liệu theo từng khách hàng
Xây dựng phiên bản PG tương tự Vitess của MySQL
Cân nhắc về FoundationDB
Cách tiếp cận xem sharding như một kiểu hack
Thắc mắc về việc không dùng extension Citus
Khả năng sử dụng Aurora Limitless
Cách hiểu về cơ sở dữ liệu NoSQL
jsonb, nhưng vì họ đã có mô hình dữ liệu tốt nên có lẽ không cần dùng nhiều.Mức độ trưởng thành của sharding và việc cân nhắc giải pháp NewSQL
Công nghệ Spanner của Google và đánh giá của Figma