MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Nhận xét truy vấn MongoDB cùng với thông tin người dùng

(Các) vấn đề

Như được viết trước đây , có một số vấn đề khi nhúng quá mức:

Vấn đề 1:Giới hạn kích thước BSON

Kể từ thời điểm viết bài này, tài liệu BSON được giới hạn ở 16MB . Nếu đạt đến giới hạn đó, MongoDB sẽ đưa ra một ngoại lệ và bạn không thể thêm nhiều nhận xét hơn và trong trường hợp xấu nhất, thậm chí không thể thay đổi tên (người dùng-) hoặc hình ảnh nếu thay đổi sẽ làm tăng kích thước của tài liệu.

Vấn đề 2:Các giới hạn và hiệu suất truy vấn

Không thể dễ dàng truy vấn hoặc sắp xếp mảng nhận xét trong các điều kiện nhất định. Một số thứ sẽ yêu cầu tổng hợp khá tốn kém, những thứ khác thì khá phức tạp.

Trong khi người ta có thể tranh luận rằng một khi các truy vấn được đặt ra, thì đây không phải là vấn đề nhiều, tôi xin phép khác. Đầu tiên, truy vấn càng phức tạp thì càng khó tối ưu hóa, cho cả nhà phát triển và sau đó là trình tối ưu hóa truy vấn MongoDBs. Tôi đã có kết quả tốt nhất với việc đơn giản hóa các mô hình dữ liệu và truy vấn, tăng tốc độ phản hồi lên hệ số 100 trong một trường hợp.

Khi mở rộng quy mô, các nguồn cung cấp lại cần thiết cho các truy vấn phức tạp và / hoặc tốn kém thậm chí có thể tổng hợp cho toàn bộ máy khi so sánh với mô hình dữ liệu đơn giản hơn và theo các truy vấn.

Vấn đề 3:Khả năng bảo trì

Cuối cùng nhưng không kém phần quan trọng, bạn cũng có thể gặp phải sự cố khi duy trì mã của mình. Như một quy tắc đơn giản của ngón tay cái

Trong ngữ cảnh này, "đắt" vừa dùng để chỉ tiền (cho các dự án chuyên nghiệp) và thời gian (cho các dự án sở thích).

(Của tôi!) Giải pháp

Nó khá dễ dàng:đơn giản hóa mô hình dữ liệu của bạn. Do đó, các truy vấn của bạn sẽ trở nên ít phức tạp hơn và (hy vọng) nhanh hơn.

Bước 1:Xác định các trường hợp sử dụng của bạn

Đó sẽ là một dự đoán hoang đường đối với tôi, nhưng điều quan trọng ở đây là chỉ cho bạn phương pháp chung. Tôi sẽ xác định các trường hợp sử dụng của bạn như sau:

  1. Đối với một bài đăng nhất định, người dùng có thể nhận xét
  2. Đối với một bài đăng nhất định, hãy hiển thị tác giả và các nhận xét, cùng với tên người dùng của người nhận xét và tác giả và ảnh của họ
  3. Đối với một người dùng nhất định, có thể dễ dàng thay đổi tên, tên người dùng và hình ảnh

Bước 2:Lập mô hình dữ liệu của bạn cho phù hợp

Người dùng

Trước hết, chúng tôi có một mô hình người dùng đơn giản

{
  _id: new ObjectId(),
  name: "Joe Average",
  username: "HotGrrrl96",
  picture: "some_link"
}

Không có gì mới ở đây, chỉ được thêm vào để hoàn thiện.

Bài đăng

{
  _id: new ObjectId()
  title: "A post",
  content: " Interesting stuff",
  picture: "some_link",
  created: new ISODate(),
  author: {
    username: "HotGrrrl96",
    picture: "some_link"
  }
}

Và đó là về nó cho một bài đăng. Có hai điều cần lưu ý ở đây:đầu tiên, chúng tôi lưu trữ dữ liệu tác giả mà chúng tôi cần ngay lập tức khi hiển thị một bài đăng, vì điều này giúp chúng tôi lưu một truy vấn cho một trường hợp sử dụng rất phổ biến, nếu không muốn nói là phổ biến. Tại sao chúng ta không lưu dữ liệu nhận xét và người nhận xét theo bản ghi? Do giới hạn kích thước 16 MB , chúng tôi đang cố gắng ngăn chặn việc lưu trữ các tham chiếu trong một tài liệu duy nhất. Thay vào đó, chúng tôi lưu trữ các tài liệu tham khảo trong các tài liệu bình luận:

Nhận xét

{
  _id: new ObjectId(),
  post: someObjectId,
  created: new ISODate(),
  commenter: {
    username: "FooBar",
    picture: "some_link"
  },
  comment: "Awesome!"
}

Tương tự như với các bài đăng, chúng tôi có tất cả các dữ liệu cần thiết để hiển thị một bài đăng.

Các truy vấn

Những gì chúng tôi đạt được bây giờ là chúng tôi đã vượt qua giới hạn kích thước BSON và chúng tôi không cần tham khảo dữ liệu người dùng để có thể hiển thị các bài đăng và nhận xét, điều này sẽ giúp chúng tôi tiết kiệm rất nhiều truy vấn. Nhưng hãy quay lại các trường hợp sử dụng và một số truy vấn khác

Thêm nhận xét

Bây giờ điều đó hoàn toàn đơn giản.

Nhận tất cả hoặc một số nhận xét cho một bài đăng nhất định

Đối với tất cả các nhận xét

db.comments.find({post:objectIdOfPost})

Đối với 3 nhận xét cuối cùng

db.comments.find({post:objectIdOfPost}).sort({created:-1}).limit(3)

Vì vậy, để hiển thị một bài đăng và tất cả (hoặc một số) nhận xét của nó bao gồm tên người dùng và hình ảnh, chúng tôi đang ở hai truy vấn. Nhiều hơn mức bạn cần trước đây, nhưng chúng tôi đã vượt qua giới hạn kích thước và về cơ bản, bạn có thể có số lượng nhận xét không giới hạn cho mỗi bài đăng. Nhưng chúng ta hãy đến một cái gì đó thực tế

Nhận 5 bài đăng mới nhất và 3 nhận xét mới nhất của họ

Đây là một quy trình gồm hai bước. Tuy nhiên, với việc lập chỉ mục thích hợp (sẽ quay lại điều đó sau), điều này vẫn sẽ nhanh (và do đó tiết kiệm tài nguyên):

var posts = db.posts.find().sort({created:-1}).limit(5)
posts.forEach(
  function(post) {
    doSomethingWith(post);
    var comments = db.comments.find({"post":post._id}).sort("created":-1).limit(3);
    doSomethingElseWith(comments);
  }
)

Nhận tất cả các bài đăng của một người dùng nhất định được sắp xếp từ mới nhất đến cũ nhất và nhận xét của họ

var posts = db.posts.find({"author.username": "HotGrrrl96"},{_id:1}).sort({"created":-1});
var postIds = [];
posts.forEach(
  function(post){
    postIds.push(post._id);
  }
)
var comments = db.comments.find({post: {$in: postIds}}).sort({post:1, created:-1});

Lưu ý rằng chúng tôi chỉ có hai truy vấn ở đây. Mặc dù bạn cần tạo kết nối "thủ công" giữa các bài đăng và nhận xét tương ứng của chúng, nhưng điều đó sẽ khá đơn giản.

Thay đổi tên người dùng

Đây có lẽ là một trường hợp sử dụng hiếm hoi được thực thi. Tuy nhiên, nó không phức tạp lắm với mô hình dữ liệu đã nói

Đầu tiên, chúng tôi thay đổi tài liệu người dùng

db.users.update(
  { username: "HotGrrrl96"},
  {
    $set: { username: "Joe Cool"},
    $push: {oldUsernames: "HotGrrrl96" }
  },
  {
    writeConcern: {w: "majority"}
  }
);

Chúng tôi đẩy tên người dùng cũ vào một mảng theo. Đây là một biện pháp bảo mật trong trường hợp xảy ra sự cố với các hoạt động sau. Hơn nữa, chúng tôi đặt mối quan tâm về việc ghi lên mức khá cao để đảm bảo dữ liệu được lâu bền.

db.posts.update(
  { "author.username": "HotGrrrl96"},
  { $set:{ "author.username": "Joe Cool"} },
  {
    multi:true,
    writeConcern: {w:"majority"}
  }
)

Không có gì đặc biệt ở đây. Tuyên bố cập nhật cho các nhận xét trông khá giống nhau. Mặc dù những truy vấn đó mất một chút thời gian, nhưng chúng hiếm khi được thực thi.

Các chỉ số

Theo quy tắc chung, người ta có thể nói rằng MongoDB chỉ có thể sử dụng một chỉ mục cho mỗi truy vấn. Mặc dù điều này không hoàn toàn đúng vì có các giao điểm chỉ mục, nhưng nó rất dễ xử lý. Một điều khác là các trường riêng lẻ trong một chỉ mục kết hợp có thể được sử dụng độc lập. Vì vậy, một cách tiếp cận dễ dàng để tối ưu hóa chỉ mục là tìm truy vấn với hầu hết các trường được sử dụng trong các hoạt động sử dụng chỉ số và tạo chỉ mục kết hợp của chúng. Lưu ý rằng thứ tự xuất hiện trong truy vấn rất quan trọng. Vì vậy, hãy tiếp tục.

Bài đăng

db.posts.createIndex({"author.username":1,"created":-1})

Nhận xét

db.comments.createIndex({"post":1, "created":-1})

Kết luận

Được thừa nhận rằng một tài liệu được nhúng đầy đủ cho mỗi bài đăng là cách nhanh nhất để tải nó và đó là nhận xét. Tuy nhiên, nó không mở rộng quy mô tốt và do bản chất của các truy vấn có thể phức tạp cần thiết để giải quyết nó, lợi thế về hiệu suất này có thể bị tận dụng hoặc thậm chí bị loại bỏ.

Với giải pháp trên, bạn đánh đổi một số tốc độ (nếu!) Với khả năng mở rộng không giới hạn về cơ bản và cách xử lý dữ liệu đơn giản hơn nhiều.

Hth.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Làm thế nào để đảm bảo một cuộc gọi không đồng bộ được thực thi trước khi trả về từ một hàm trong Mongoose?

  2. Tìm tổng thời gian của một người dùng trong mongoDB

  3. Mongoose không lưu đối tượng lồng nhau

  4. mongodb tổng hợp php

  5. Không thể xác định thông tin tuần tự hóa cho * biểu thức * bằng cách sử dụng .Date