Tránh lưu trữ các mảng lớn và cách thay thế hiệu quả trong Mongodb

Nội dung bài viết

Video học lập trình mỗi ngày

Một trong những quy tắc chung khi lập mô hình dữ liệu trong MongoDB là dữ liệu được truy cập cùng nhau phải được lưu trữ cùng nhau. Đó là điều quan tâm đầu tiên khi lập trình database.

Khuyến nghị: Học - Series RESTful APIs Mongodb

Massive Arrays

Nếu các bạn chưa nghe đến cụm từ "Massive Arrays" thì bài này các bạn sẽ hiểu được điều đó. Series RESTful APIs Mongodb đã đi được 5 bài, nhưng đến phần thiết kế model Mongodb thì chúng ta phải tìm hiểu lại cách thiết kế như thế nào là hiệu quả. Ta sẽ đi ví dụ sau:

Nếu bạn thường xuyên truy xuất hoặc cập nhật dữ liệu cùng nhau, bạn có thể nên lưu trữ chúng cùng nhau, đó là điều hiển nhiên, nhưng đôi khi chúng ta lại lợi dụng điều này để đi quá xa, làm cho một thông tin chứa quá nhiều dữ liệu như tình huống dưới đây.

Hãy xem xét một mô hình thiết kế lưu trữ thông tin về những đơn hàng được lưu trữ trong các kho hàng khác nhau. Nếu chúng tôi push các đơn hàng vào kho hàng thì chúng tôi có dữ liệu như sau:

// Kho hàng
{
   "_id": "Kho 1",
   "name": "Tòa nhà Viettel,
   "city": "Hồ Chí Minh",
   "state": "active",
   "orders": [
      {
         "_id": "Đơn 1",
         "name": "Đồng Hồ Casio",
         "time-order": "12/09/2021:00:00:00",
         "price": "100.000",
         "userId": "user-01"
      },
      {
         "_id": "Đơn 2",
         "name": "Giày Bitis Hunter",
         "time-order": "12/08/2021:00:00:00",
         "price": "8.000",
         "userId": "user-03"
      },
   ]
}

Trong việc thiết kế như vậy chúng ta đã phạm phải một số sai lầm, chúng ta đều biết số lượng orders là không giới hạn, và một thời gian sẽ trở thành dữ liệu khổng lồ trong bảng kho hàng vì tối đa mỗi collection trong Mongodb chỉ chiếm 16Mb. Ngoài ra, việc đọc và xây dựng chỉ mục trên mảng dần trở nên kém hiệu quả hơn khi kích thước mảng tăng lên. Vậy chúng ta phải làm cách nào?

Cách triển khai mô hình "massive arrays anti-pattern"

Để thay đổi những nhược điểm trên chúng ta sẽ thay đổi như sau, thay vì nhúng đơn hàng vào kho hàng thì chúng ta lật lại thành đưa kho hàng vào đơn hàng như sau:

// Đơn hàng Collection

{
    "_id": "Đơn 1",
    "name": "Đồng Hồ Casio",
    "time-order": "12/09/2021:00:00:00",
    "price": "100.000",
    "userId": "user-01",
    "kho hang": {
        "_id": "Kho 1",
        "name": "Tòa nhà Viettel,
        "city": "Hồ Chí Minh",
        "state": "active",
    }
},
{
    "_id": "Đơn 2",
    "name": "Giày Bitis Hunter",
    "time-order": "12/08/2021:00:00:00",
    "price": "8.000",
    "userId": "user-03",
    "kho hang": {
        "_id": "Kho 1",
        "name": "Tòa nhà Viettel,
        "city": "Hồ Chí Minh",
        "state": "active",
    }
}

Chúng ta có thể nhìn qua có vẻ hợp lý nhưng không, đó không phải là điều chúng ta muốn. Hãy nhìn kỹ lại một chút, chúng ta đang bị lạp lại thông tin kho hàng một cách thừa thãi. Và điều quan trọng nếu như chúng ta thay đổi thông tin kho hàng thì bắt buộc chúng ta phải update lại hết toàn bộ đơn hàng hay sao? Giả sử như kho hàng không bao giờ thay đổi or ít thay đổi thì đó là cách nghe có vẻ hợp lý.

Tiếp theo nếu một số trường hợp truy vấn không nhất thiết phải hiển thị thông tin đơn hàngkho hàng cùng nhau, thì cách lưu trữ này tốn tài nguyên, do đó chúng tôi sẽ tách ra hai collection và tham chiếu đến với nhau.

// Kho hàng collection
{
    "_id": "Kho 1",
    "name": "Tòa nhà Viettel,
    "city": "Hồ Chí Minh",
    "state": "active",
}

// đơn hàng collection
{
    "_id": "Đơn 1",
    "name": "Đồng Hồ Casio",
    "time-order": "12/09/2021:00:00:00",
    "price": "100.000",
    "userId": "user-01",
    "kho_hang_id": "Kho 1"
},
{
    "_id": "Đơn 2",
    "name": "Giày Bitis Hunter",
    "time-order": "12/08/2021:00:00:00",
    "price": "8.000",
    "userId": "user-03",
    "kho_hang_id": "Kho 1"
}

Ở đây chúng tôi đã hoàn toàn tách biệt dữ liệu của mình. Chúng tôi đã loại bỏ các mảng lớn và chúng tôi không có sự trùng lặp dữ liệu, rất tốt. Và hạn chế của điều này là chúng ta phải dùng $lookup để kết hợp dữ liệu với nhau. Nếu như các truy vấn $lookup diễn ra thường xuyên thì rât tốn kém. Vì vậy điều quan trọng chúng ta là phải xem xét tần suất bạn sẽ cần thực hiện.

Dừng lại ở đây và suy nghĩ, hãy nhìn vào object của kho hàng chúng ta có thể thấy state kho hàng hiếm khi thay đổi. Do đó, chúng ta nên thay đổi và lưu thêm những giá trị ít thay đổi nhằm phục vụ truy vấn cho nhanh. Hãy xem xét thêm trường hợp này.

// Kho hàng collection
{
    "_id": "Kho 1",
    "name": "Tòa nhà Viettel,
    "city": "Hồ Chí Minh",
    "state": "active",
}

// đơn hàng collection
{
    "_id": "Đơn 1",
    "name": "Đồng Hồ Casio",
    "time-order": "12/09/2021:00:00:00",
    "price": "100.000",
    "userId": "user-01",
    "kho_hang": {
        id: "Kho 1",
        "state": "active"
    }
},
{
    "_id": "Đơn 2",
    "name": "Giày Bitis Hunter",
    "time-order": "12/08/2021:00:00:00",
    "price": "8.000",
    "userId": "user-03",
    "kho_hang": {
        id: "Kho 1",
        "state": "active"
    }
}

Trong trường hợp cụ thể này, state kho hàng hiếm khi thay đổi, vì vậy giải pháp này có hiệu quả.

Tóm lại

Đương nhiện là mỗi công ty mỗi cá nhân đều có quan điểm riêng. Nhưng đây có thể là một chút ít tham khảo giúp chúng ta hoàn thiện hơn trong khi lập mô hình trong database. Các bạn có thể quay trở lại với Series RESTful APIs Mongodb để chúng ta có thể tiếp tục.

Ref: Tài nguyên Mongodb

Có thể bạn đã bị missing