Tại DailyWatch, bảng "video liên quan" của chúng tôi bắt đầu từ một truy vấn SQLite đơn giản. Nó kết hợp ba bảng, đếm các thẻ (tags) trùng lặp và trả về một danh sách được xếp hạng. Đối với một danh mục nhỏ, bấy nhiêu là đủ. Một video nấu ăn được gắn thẻ "italian" và "pasta" sẽ hiển thị các video khác có cùng thẻ đó, và người dùng sẽ nhấp vào. Kết quả trông có vẻ liên quan vì siêu dữ liệu (metadata) sạch sẽ và thư viện chưa có chiều sâu.
Sau đó, danh mục phát triển và kỳ vọng của khán giả cũng thay đổi. Mọi người không chỉ đơn thuần muốn xem thêm các video có thẻ giống hệt nhau. Họ muốn xem clip mà 40% người xem đã xem ngay sau video hiện tại. Họ muốn tìm thấy những kênh ngách liên tục xuất hiện trong cùng một phiên xem đêm khuya, ngay cả khi phần mô tả của kênh đó không đề cập gì đến video đang xem và các thẻ của nó rất sơ sài. Đây là các mô hình hành vi, chứ không phải là sự trùng khớp về siêu dữ liệu. SQLite không thể diễn đạt được chúng nếu không biến thành một mớ hỗn độn khó bảo trì của các phép self-join và các biểu thức bảng chung đệ quy (recursive common table expressions). Mỗi tín hiệu mới mà chúng tôi cố gắng mô hình hóa, cho dù là việc cùng xem (co-viewership) hay tính liền kề của phiên xem (session adjacency), đều làm tăng độ trễ và gánh nặng về mặt tư duy. Chúng tôi đang có một bài toán đồ thị bị ép vào khuôn khổ của mô hình quan hệ.
Tôi đã chuyển lớp gợi ý sang Apache AGE.
Apache AGE là một tiện ích mở rộng của PostgreSQL giúp bổ sung các truy vấn đồ thị openCypher vào cơ sở dữ liệu mà bạn đang chạy. Nó không phải là một máy chủ riêng biệt. Nó không phải là một sidecar. Nó chạy ngay bên trong Postgres, điều này có nghĩa là chúng tôi có thể xây dựng một mạng lưới cùng xem mà không cần phải thiết lập một cụm Neo4j chuyên dụng hay phải học một quy trình vận hành hoàn toàn mới. Đối với một đội ngũ nhỏ không có kỹ sư chuyên về độ tin cậy cơ sở dữ liệu (DBRE), sự khác biệt đó là cực kỳ quan trọng.
Tại sao một Tiện ích mở rộng lại tốt hơn một Cơ sở dữ liệu mới
Thêm một cơ sở dữ liệu đồ thị vào ngăn xếp (stack) của bạn thì rất dễ dàng trên bảng trắng nhưng lại rất tốn kém khi triển khai thực tế. Bạn sẽ cần các bảng điều khiển giám sát mới, quy trình sao lưu mới, các pool kết nối mới và logic dự phòng (failover) mới. AGE né tránh được tất cả những điều đó vì nó nằm ngay bên trong thực thể Postgres hiện có của bạn.
Có bốn lý do thực tế khiến phương pháp này hiệu quả với chúng tôi.
- Không cần hạ tầng mới. Vì AGE là một tiện ích mở rộng, lịch trình
pg_dumphiện tại, các bản sao (replicas) hiện có và các bước kiểm tra sức khỏe Postgres tiêu chuẩn của bạn đều tiếp tục hoạt động bình thường. Bạn không cần phải thuyết phục đội vận hành phải chăm sóc thêm một kho lưu trữ dữ liệu khác. - Kết hợp các khối lượng công việc trong một truy vấn. AGE cho phép bạn viết Cypher ngay bên trong SQL. Bạn có thể chạy một phép duyệt đồ thị để tìm các video ứng viên, sau đó kết hợp (join) tập kết quả đó với bảng
usersquan hệ để thực thi các hạn chế về nội dung theo khu vực, hoặc với bảngsponsorshipsquan hệ để giảm ưu tiên một số kênh nhất định. Chỉ một lượt trao đổi (round-trip). Hai ngôn ngữ truy vấn cùng hợp tác. - Tính di động. Cypher là một ngôn ngữ truy vấn đồ thị mở và được tài liệu hóa tốt. Nếu DailyWatch phát triển vượt quá khả năng của AGE và cần chuyển sang Neo4j hoặc Memgraph sau này, logic truy vấn có thể được chuyển đổi với việc viết lại tối thiểu. Bạn không bị khóa vào một phương ngữ độc quyền.
- Tính tương thích. Vì dữ liệu cuối cùng nằm trong PostgreSQL, các công cụ PHP hoặc Python hiện có của bạn sẽ không thay đổi. Bạn kết nối bằng cùng một driver, xử lý cùng một chuỗi kết nối và lấy các hàng dữ liệu theo cùng một cách. Logic đồ thị nằm ở lớp truy vấn, không phải ở lớp ứng dụng.
Mô hình hóa việc cùng xem
Việc triển khai rất đơn giản. Chúng tôi định nghĩa các nút (nodes) cho các thực thể mà chúng tôi quan tâm: Video và Channel. Sau đó, chúng tôi định nghĩa các cạnh (edges) cho các mối quan hệ giữa chúng. Một cạnh PUBLISHED liên kết một Channel với một Video. Một cạnh CO_VIEWED liên kết một Video với một Video khác, mang theo một thuộc tính weight đại diện cho tần suất hai video đó xuất hiện trong cùng một phiên xem.
Mô hình này nắm bắt được thứ mà SQL dựa trên thẻ không thể làm được: cấu trúc ngầm định
