MongoDB sang Postgres

LƯU Ý: Bài đăng blog này là sự kết hợp của MongoDB sang Postgres (2024-03-06)Sequelize so với Prisma (2023-05-25). Các blog gốc đã bị gỡ bỏ, và blog này đã thay thế chúng vì cả hai đều chứa về cơ bản cùng một nội dung/thông tin. Việc di chuyển bắt đầu vào đầu tháng 3 năm 2023, việc chuyển đổi xảy ra vào giữa tháng 11 năm 2023, và tất cả các phiên bản của hệ thống MongoDB cũ đã được tắt hoàn toàn vào đầu tháng 1 năm 2024.

Giới thiệu

Trong thời gian làm việc tại eBay, tôi đã đối mặt với điều trở thành vấn đề khó khăn nhất về mặt kỹ thuật trong sự nghiệp của mình: di chuyển Hệ thống Quản lý Lưu trữ (STMS) từ MongoDB sang Postgres. Đây không chỉ là một cuộc thay đổi cơ sở dữ liệu đơn giản; đó là một sự chuyển đổi kiến trúc hoàn toàn của một hệ thống quan trọng, hệ thống tiếp nhận hơn 1,5 triệu chỉ số mỗi phút trên khắp các trung tâm dữ liệu của eBay, với yêu cầu không có thời gian ngừng hoạt động và duy trì gần như toàn bộ chức năng hiện có.

STMS là gì?

Hệ thống Quản lý Lưu trữ (STMS) đóng vai trò là một công cụ nội bộ quan trọng cho nhóm Cơ sở hạ tầng Dịch vụ & Lưu trữ (SSI) của eBay. Nó giám sát và quản lý các thiết bị trên khắp các trung tâm dữ liệu của eBay, cho phép các kỹ sư:

  • Giám sát các chỉ số từ hàng chục mảng lưu trữ, switch, máy chủ, nhóm đĩa và cụm
  • Xử lý cảnh báo cho switch và mảng lưu trữ
  • Hoàn thành các tác vụ nâng cao như phân bổ máy chủ
  • Truy cập dữ liệu thời gian thực cho các dịch vụ nội bộ khác của eBay

STMS bao gồm hơn 70 mảng lưu trữ, 60 switch, 1100 máy chủ, 900 nhóm đĩa và 200 cụm trên 3 trung tâm dữ liệu của eBay. Với vai trò thiết yếu trong hạ tầng của eBay, bất kỳ thời gian ngừng hoạt động hay mất chức năng nào đều sẽ ảnh hưởng trực tiếp đến các dịch vụ cốt lõi và hoạt động kinh doanh của công ty.

Thách thức

Tại sao việc di chuyển là cần thiết

Quyết định di chuyển từ MongoDB sang Postgres không được đưa ra một cách dễ dàng. Trong khi MongoDB ban đầu phục vụ STMS rất tốt, độ phức tạp ngày càng tăng của các mối quan hệ dữ liệu và nhu cầu về khả năng truy vấn tinh vi hơn đã khiến Postgres trở thành một giải pháp dài hạn tốt hơn cho trường hợp sử dụng của chúng tôi.

Điều gì khiến vấn đề này trở nên khó khăn

Độ phức tạp của việc di chuyển này bắt nguồn từ một số thách thức cơ bản:

1. Sự khác biệt cơ bản giữa các cơ sở dữ liệu MongoDB và Postgres là những cơ sở dữ liệu hoàn toàn khác nhau. MongoDB là cơ sở dữ liệu dựa trên tài liệu (NoSQL), nghĩa là dữ liệu được lưu dưới dạng JSON trong các collection, giống như các tài liệu trong một tủ hồ sơ. Postgres là cơ sở dữ liệu quan hệ (SQL), nghĩa là dữ liệu được lưu dưới dạng các hàng trong các bảng, giống như trong một bảng tính.

2. Kiến trúc của mã nguồn Toàn bộ backend của STMS được xây dựng để xử lý và quản lý dữ liệu dưới dạng JSON, sử dụng các gói thư viện chỉ tương thích với MongoDB cho các thao tác cơ sở dữ liệu. Điều này có nghĩa là không chỉ thay đổi cơ sở dữ liệu, mà còn phải cấu trúc lại cách toàn bộ ứng dụng của chúng tôi xử lý dữ liệu.

3. Yêu cầu không có thời gian ngừng hoạt động Do STMS quan trọng như thế nào với tư cách là một công cụ nội bộ, nên không được phép có thời gian ngừng hoạt động trong quá trình di chuyển. Hệ thống phải tiếp tục phục vụ hơn 1,5 triệu chỉ số mỗi phút trong suốt toàn bộ quá trình.

4. Thời gian gấp & kinh nghiệm hạn chế Việc di chuyển phải hoàn thành trong vòng vài tháng, ban đầu không có kế hoạch thực thi rõ ràng. Cả tôi lẫn đồng nghiệp đều không có kinh nghiệm di chuyển một mã nguồn kế thừa lớn từ các cơ sở dữ liệu NoSQL sang SQL, và bản thân tôi cũng có ít kinh nghiệm trước đó với Postgres.

5. Quy mô & độ phức tạp Việc di chuyển bao gồm chuyển đổi 36 collection của MongoDB thành 74 bảng Postgres, đòi hỏi phải cân nhắc cẩn thận về các mối quan hệ, lập chỉ mục và tối ưu hóa truy vấn.

Chọn ORM phù hợp: Sequelize so với Prisma

Một trong những quyết định lớn đầu tiên là chọn một công cụ ORM (Object-Relational Mapping). Vì mã nguồn của chúng tôi đã được thiết kế để sử dụng Mongoose cho MongoDB, nên việc dùng ORM sẽ mang lại con đường chuyển đổi mượt mà nhất.

Phân tích yêu cầu

Sau khi phân tích cẩn thận nhu cầu của dự án, tôi đã xác lập các tiêu chí thiết yếu cho bất kỳ giải pháp ORM nào:

  • Phải là một gói JavaScript (phần lớn mã của chúng tôi được viết bằng JavaScript)
  • Phải hỗ trợ Postgres và hầu hết các tính năng của nó
  • Hiệu năng phải ít nhất ngang bằng hoặc tốt hơn Mongoose
  • Phải là mã nguồn mở và được duy trì

Các ứng viên

Sau quá trình nghiên cứu sâu rộng, tôi thu hẹp còn hai ứng viên chính: Sequelize và Prisma. Tôi đã tạo các môi trường kiểm thử toàn diện bằng Docker cho Postgres và chuyển đổi tập dữ liệu lớn nhất, phức tạp nhất của chúng tôi từ cấu trúc tài liệu sang cấu trúc bảng.

Phương pháp kiểm thử

Đối với mỗi ORM, tôi đo hiệu năng trên các thao tác quan trọng:

  • Thời gian tạo một mục
  • Thời gian cập nhật một mục
  • Thời gian cập nhật các mục lồng nhau (các mối quan hệ và cặp khóa-giá trị JSON)
  • Thời gian xóa một mục
  • Thời gian truy vấn/lấy một mục

Quyết định: Sequelize

Khoảng ngày 15 tháng 5 năm 2023, tôi quyết định rằng Sequelize là ORM tốt hơn cho trường hợp sử dụng của chúng tôi. Đây là lý do:

Ưu điểm của Sequelize:

  • Thực sự là mã nguồn mở và không được duy trì bởi một startup có vốn đầu tư
  • Hỗ trợ hầu hết các tính năng của Postgres
  • Hiệu năng tốt hơn, đặc biệt so với Prisma
  • Hệ sinh thái trưởng thành với hơn 10 năm phát triển
  • Biểu diễn model/schema linh hoạt bằng các lớp JavaScript
  • Hỗ trợ các phép join và tùy chọn lọc phức tạp, bao gồm Regex

Kết quả hiệu năng:

Trong các thử nghiệm của tôi, Sequelize vượt trội đáng kể so với Prisma. Với các mục trong tập dữ liệu lớn của chúng tôi:

  • Sequelize: ~2,26 giây mỗi mục
  • Prisma: ~11,21 giây mỗi mục

Prisma chậm hơn khoảng 5 lần so với Sequelize cho trường hợp sử dụng của chúng tôi. Ngoài ra, việc xóa một mục khỏi tập dữ liệu lớn nhất của chúng tôi với Prisma mất gần 4 phút, điều này là không thể chấp nhận được đối với yêu cầu của chúng tôi.

Thách thức của Sequelize:

  • Biểu diễn model phức tạp và cồng kềnh hơn (564 dòng so với 262 dòng của Mongoose)
  • Cú pháp khó hiểu trong một số trường hợp
  • Độ phức tạp khi di chuyển cơ sở dữ liệu
  • Tài liệu không toàn diện bằng Prisma

So sánh ưu & nhược điểm của Sequelize & Prisma

Để đưa ra cái nhìn đầy đủ hơn về lý do tôi chọn Sequelize, tôi muốn chia sẻ chi tiết các ưu và nhược điểm mà tôi đã tổng hợp cho cả hai ORM trong quá trình đánh giá. Tôi cũng xem xét chúng ở khía cạnh biểu diễn schema và hỗ trợ cộng đồng tính đến ngày 15 tháng 5 năm 2023. Việc đào sâu hơn này đã giúp củng cố lựa chọn của tôi, và tôi hy vọng nó cũng có thể hữu ích cho bất kỳ ai khác đang đối mặt với một quyết định tương tự.

Ưu điểm của Sequelize:

  • Có hàm sync() tự động tạo và xử lý các bảng cho bạn, giúp tiết kiệm rất nhiều công sức thủ công.
  • Có thể xử lý các phép join phức tạp cho dữ liệu lồng nhau, điều này rất quan trọng đối với cấu trúc của STMS.
  • Hỗ trợ nhiều tùy chọn lọc khác nhau, bao gồm Regex, mang lại sự linh hoạt trong truy vấn.
  • Biểu diễn model/schema được thực hiện bằng JavaScript thô thông qua các lớp, vốn có thể tùy biến cao để phù hợp với nhu cầu cụ thể.
  • Xử lý kết nối cơ sở dữ liệu một cách liền mạch, bao gồm hỗ trợ nhiều kết nối đọc.
  • Hỗ trợ các truy vấn SQL thô khi bạn cần đi sâu vào bên trong.
  • Thống kê cộng đồng tính đến ngày 15 tháng 5 năm 2023: Trên NPM, được cập nhật lần cuối 14 ngày trước với 1.505.835 lượt tải hàng tuần; trên GitHub, được cập nhật lần cuối hôm qua với 4,2k Fork và 27,9k Sao. Nó là mã nguồn mở với giấy phép MIT trong hơn 10 năm, vì vậy tôi tin chắc nó sẽ tiếp tục như vậy.

Nhược điểm của Sequelize:

  • Biểu diễn model/schema có thể trở nên rất phức tạp và cồng kềnh. Ví dụ, trong khi biểu diễn Mongoose của tập dữ liệu lớn của chúng tôi khoảng 262 dòng (bao gồm cả khoảng trắng), cùng tập dữ liệu đó trong Sequelize phình lên thành 564 dòng.
  • Cú pháp có thể gây nhầm lẫn và phức tạp trong một số tình huống, điều này đôi khi làm tôi chậm lại.
  • Việc di chuyển hoặc chỉnh sửa cơ sở dữ liệu khá phiền phức. Ngay cả khi sequelize-cli tạo các script di chuyển, nó vẫn rườm rà, mặc dù tôi nhận thấy đây là một điểm đau phổ biến với hầu hết các ORM.
  • Tài liệu không quá tốt, dù đang được cải thiện. May mắn thay, các công cụ như ChatGPT có hiểu biết khá vững về Sequelize nhờ lịch sử lâu dài của nó, điều này đã giúp lấp đầy các khoảng trống.
  • Không nhạy cảm kiểu bằng Prisma, điều này có thể dẫn đến vấn đề trong một số dự án.
  • Hỗ trợ TypeScript hạn chế, dù điều này không phải là mối lo với STMS, nhưng có thể là yếu tố quyết định đối với người khác.

Ưu điểm của Prisma:

  • Sử dụng ngôn ngữ schema riêng, giúp việc tạo model gọn gàng và súc tích hơn. Để so sánh, trong khi Mongoose cần 262 dòng cho tập dữ liệu lớn của chúng tôi, Prisma chỉ cần 221 dòng.
  • Đi kèm công cụ CLI giúp đơn giản hóa việc tạo và di chuyển cơ sở dữ liệu, đây là công cụ tốt nhất tôi từng thấy từ một ORM cho đến nay, dù chưa hoàn hảo.
  • Hỗ trợ các truy vấn SQL thô, mang lại tính linh hoạt khi cần.
  • Cú pháp mã rõ ràng và đơn giản hơn so với Sequelize, giúp dễ học hơn.
  • Tự động tạo trình xây dựng truy vấn cho Node.js và TypeScript thông qua client của nó, đây là một điểm cộng.
  • Có tài liệu xuất sắc và rõ ràng. ChatGPT không cập nhật tốt bằng với Prisma, nhưng tài liệu chính thức thường bù đắp điều đó.
  • Thống kê cộng đồng tính đến ngày 15 tháng 5 năm 2023: Trên NPM, được cập nhật lần cuối 6 ngày trước với 1.344.705 lượt tải hàng tuần; trên GitHub, được cập nhật lần cuối 3 giờ trước với 1,1k Fork và 31,3k Sao.

Nhược điểm của Prisma:

  • Không hỗ trợ lọc Regex cho Postgres, dù có các lựa chọn thay thế như “contains”, “includes” và “startsWith.”
  • Hiệu năng là một vấn đề lớn trong các thử nghiệm của tôi. Việc tạo mục cho tập dữ liệu lớn của chúng tôi với Prisma mất khoảng 11,21 giây mỗi mục so với 2,26 giây của Sequelize, chậm hơn khoảng 5 lần.
  • Xóa một mục đơn lẻ khỏi tập dữ liệu lớn mất gần 4 phút, đây là một điểm không thể chấp nhận đối với nhu cầu của chúng tôi.
  • Ngay cả với một so sánh công bằng trên một tập dữ liệu quan hệ ba lớp sâu phức tạp, Sequelize vẫn nhanh hơn đáng kể khi xóa.
  • Prisma được hậu thuẫn bởi một startup có 56,5 triệu đô la vốn tài trợ. Mặc dù mã ORM chính của nó là mã nguồn mở theo Apache-2.0, tôi vẫn lo ngại về các thay đổi giấy phép tiềm năng trong tương lai, tương tự như những gì đã xảy ra với MongoDB.

Những so sánh chi tiết này khiến tôi thấy rõ rằng Sequelize phù hợp hơn với nhu cầu của STMS, đặc biệt là về hiệu năng và độ tin cậy lâu dài. Nhưng tôi nghĩ việc trình bày theo cách này có thể giúp những người khác đang phải vật lộn với lựa chọn tương tự cho các dự án của họ.

Quá trình di chuyển

Chuyển đổi cấu trúc dữ liệu

Việc chuyển đổi từ cấu trúc tài liệu của MongoDB sang cấu trúc quan hệ của Postgres đòi hỏi phải lập kế hoạch cẩn thận. Tôi đã phải:

  1. Phân tích các mối quan hệ: Xác định cách các tài liệu MongoDB liên hệ với nhau và thiết kế các mối quan hệ khóa ngoại phù hợp
  2. Chuẩn hóa dữ liệu: Tách các tài liệu lồng nhau thành các bảng riêng biệt khi phù hợp
  3. Giữ lại các tính năng JSON: Sử dụng các cột JSONB cho dữ liệu thực sự phi cấu trúc cần duy trì tính linh hoạt
  4. Thiết kế chỉ mục: Tạo các chỉ mục phù hợp cho hiệu năng truy vấn

Các giải pháp tùy chỉnh

Việc di chuyển đòi hỏi phát triển một số giải pháp tùy chỉnh:

1. Các script di chuyển dữ liệu
Tôi đã tạo các script toàn diện để:

  • Trích xuất dữ liệu từ các collection MongoDB
  • Chuyển đổi cấu trúc tài liệu sang định dạng quan hệ
  • Nhập dữ liệu vào các bảng Postgres với các mối quan hệ phù hợp

2. Lớp tương thích API
Để duy trì thời gian ngừng hoạt động bằng không, tôi đã xây dựng một lớp tương thích có thể:

  • Điều hướng yêu cầu đến MongoDB hoặc Postgres tùy theo trạng thái di chuyển
  • Đảm bảo tính nhất quán dữ liệu trong giai đoạn chuyển tiếp
  • Cung cấp các cơ chế dự phòng

3. Middleware tùy chỉnh
Phát triển middleware để xử lý sự khác biệt trong cách MongoDB và Postgres xử lý một số thao tác nhất định, đảm bảo các điểm cuối API hiện có tiếp tục hoạt động mà không cần sửa đổi.

Vượt qua các thách thức kỹ thuật

Xử lý các mối quan hệ phức tạp

Một trong những thách thức lớn nhất là chuyển đổi các tài liệu nhúng của MongoDB thành các mối quan hệ của Postgres. Ví dụ, một tài liệu MongoDB đơn lẻ có thể chứa:

  • Các thuộc tính cơ bản
  • Các đối tượng lồng nhau đại diện cho các thực thể liên quan
  • Các mảng tài liệu nhúng

Điều này phải được phân tách cẩn thận thành:

  • Các bảng chính cho các thực thể chính
  • Các bảng liên kết cho các mối quan hệ nhiều-nhiều
  • Các mối quan hệ khóa ngoại cho các liên kết một-nhiều

Tối ưu hóa truy vấn

Các kiểu truy vấn của MongoDB không chuyển trực tiếp sang SQL. Tôi đã phải:

  • Viết lại các pipeline tổng hợp phức tạp thành các phép join SQL
  • Tối ưu hóa chỉ mục cho các kiểu truy vấn mới
  • Đảm bảo hiệu năng truy vấn đáp ứng hoặc vượt hiệu năng của MongoDB

Tính toàn vẹn dữ liệu

Đảm bảo tính toàn vẹn dữ liệu trong quá trình di chuyển đòi hỏi:

  • Các script xác thực toàn diện
  • Các quy trình khôi phục
  • Đồng bộ hóa dữ liệu thời gian thực trong các giai đoạn chuyển tiếp

Kết quả & tác động

Việc di chuyển STMS từ MongoDB sang Postgres đã được hoàn tất thành công với thời gian ngừng hoạt động bằng không trong khi vẫn duy trì gần như toàn bộ các tính năng và chức năng. Kết quả vượt quá mong đợi:

Cải thiện hiệu năng:

  • Hiệu năng truy vấn được cải thiện cho các truy vấn quan hệ phức tạp
  • Tính nhất quán và toàn vẹn dữ liệu tốt hơn
  • Sử dụng dung lượng lưu trữ hiệu quả hơn

Lợi ích vận hành:

  • Nâng cao khả năng giám sát và gỡ lỗi
  • Tích hợp tốt hơn với các công cụ dựa trên SQL hiện có của eBay
  • Cải thiện các quy trình sao lưu và khôi phục

Tác động đến nhóm:

  • Nâng cao kiến thức của nhóm về cơ sở dữ liệu quan hệ
  • Thiết lập các mẫu cho các lần di chuyển cơ sở dữ liệu trong tương lai
  • Tạo ra các công cụ và quy trình có thể tái sử dụng

Các kỹ năng kỹ thuật đã đạt được

Dự án này đã mở rộng đáng kể chuyên môn kỹ thuật của tôi:

Công nghệ cơ sở dữ liệu:

  • Hiểu biết sâu sắc về các tính năng và tối ưu hóa của Postgres
  • Tối ưu hóa truy vấn SQL và tinh chỉnh hiệu năng
  • Các mẫu thiết kế cơ sở dữ liệu và chuẩn hóa
  • Cấu hình cơ sở dữ liệu primary-standby

Công cụ phát triển:

  • ORM Sequelize và xây dựng truy vấn
  • Chiến lược di chuyển cơ sở dữ liệu
  • Phương pháp kiểm thử hiệu năng
  • Xác thực dữ liệu và kiểm tra tính toàn vẹn

Các mẫu kiến trúc:

  • Chiến lược di chuyển không gián đoạn
  • Các lớp tương thích API
  • Các mẫu trừu tượng hóa cơ sở dữ liệu
  • Hệ thống giám sát và cảnh báo

Phát triển cá nhân & nghề nghiệp

Dự án di chuyển này đã mang tính chuyển đổi đối với sự phát triển sự nghiệp của tôi. Nó đã đưa tôi vào vùng chưa từng trải qua, đòi hỏi:

Kỹ năng lãnh đạo:

  • Dẫn dắt một dự án kỹ thuật phức tạp mà không có kinh nghiệm trước đó
  • Đưa ra các quyết định kiến trúc quan trọng dưới áp lực
  • Điều phối với nhiều nhóm và các bên liên quan

Khả năng giải quyết vấn đề:

  • Phân rã các vấn đề phức tạp thành các thành phần có thể quản lý được
  • Phát triển các giải pháp sáng tạo cho những thách thức chưa từng có
  • Cân bằng nhiều yêu cầu và ràng buộc cạnh tranh

Giao tiếp & làm việc nhóm:

  • Giải thích các khái niệm kỹ thuật cho các bên liên quan không chuyên kỹ thuật
  • Ghi chép các quy trình và quyết định để tham khảo trong tương lai
  • Hướng dẫn các thành viên trong nhóm về công nghệ và mẫu mới

Bài học rút ra

Bài học kỹ thuật

  1. Việc chọn cơ sở dữ liệu rất quan trọng: Lựa chọn giữa NoSQL và SQL nên dựa trên các trường hợp sử dụng cụ thể và yêu cầu dài hạn
  2. Kiểm thử hiệu năng là then chốt: Các lợi thế về lý thuyết không phải lúc nào cũng chuyển thành cải thiện hiệu năng trong thực tế
  3. Lập kế hoạch di chuyển: Lập kế hoạch và kiểm thử toàn diện là điều thiết yếu cho các lần di chuyển phức tạp
  4. Đầu tư vào công cụ: Xây dựng công cụ phù hợp ngay từ đầu giúp tiết kiệm đáng kể thời gian và giảm lỗi

Bài học quản lý dự án

  1. Giao tiếp với các bên liên quan: Cập nhật thường xuyên và giao tiếp rõ ràng giúp ngăn ngừa hiểu lầm
  2. Quản lý rủi ro: Có các kế hoạch dự phòng và quy trình khôi phục là điều thiết yếu
  3. Quản lý thời gian: Dành thêm thời gian đệm cho các thách thức bất ngờ và đường cong học tập
  4. Tài liệu hóa: Tài liệu hóa kỹ lưỡng cho phép chuyển giao kiến thức và bảo trì trong tương lai

Kết luận

Việc di chuyển STMS từ MongoDB sang Postgres là vấn đề kỹ thuật thách thức và đáng giá nhất mà tôi đã giải quyết trong sự nghiệp của mình. Nó đòi hỏi không chỉ chuyên môn kỹ thuật, mà còn cả khả năng lãnh đạo, lập kế hoạch và thích ứng. Thành công của dự án cho thấy rằng với sự lập kế hoạch đúng đắn, kiểm thử kỹ lưỡng và cam kết hướng tới sự xuất sắc, ngay cả những thách thức kỹ thuật phức tạp nhất cũng có thể được vượt qua.

Trải nghiệm này đã thay đổi cơ bản cách tôi tiếp cận kỹ thuật phần mềm, nhấn mạnh tầm quan trọng của:

  • Hiểu đầy đủ bối cảnh và yêu cầu trước khi đưa ra quyết định kỹ thuật
  • Đầu tư thời gian vào công cụ và kiểm thử phù hợp
  • Duy trì giao tiếp rõ ràng trong suốt các dự án phức tạp
  • Sẵn sàng học các công nghệ và phương pháp mới khi cần thiết

Thành công của việc di chuyển không chỉ cải thiện năng lực của STMS mà còn thiết lập các mẫu và quy trình tiếp tục mang lại lợi ích cho các dự án hạ tầng của eBay. Nó củng cố niềm tin của tôi rằng đón nhận những thách thức chưa biết và thành công vượt qua chúng là chìa khóa cho cả phát triển cá nhân lẫn nghề nghiệp.

Nhìn lại, dự án này đại diện cho một bước ngoặt trong sự nghiệp của tôi, biến tôi từ một nhà phát triển triển khai giải pháp thành một kỹ sư có thể kiến trúc và dẫn dắt các sáng kiến kỹ thuật phức tạp. Sự tự tin và các kỹ năng có được từ trải nghiệm này tiếp tục định hướng cách tôi tiếp cận những thách thức và cơ hội mới trong kỹ thuật phần mềm.