Tạo API server trên Node.js với Express và MongoDB (part 2)

Tram Ho

Ảnh minh họa: Khi collection có 200 nghìn documents

Xin chào mọi người, tiếp tục với bài viết lần trước: Tạo API với Express.js + Mongodb. Mình cũng nhận được request như muốn hướng dẫn build cả API và giao diện trọn vẹn. Tuy nhiên mình cũng chưa viết thành tut được.

Ngoài ra cũng có một issue mà mình và một số bạn có thể gặp phải qua bài viết lần trước đó là về việc phân trang khi lấy danh sách posts. Với ứng dụng lưu trữ ít, chỉ vài trăm nghìn document trong một collection thì có vẻ mọi thứ vẫn tốt đẹp như trên. Nhưng, khi kích thước tăng lên từ 1 triệu documents, bạn đã thấy khác biệt khi thời gian phản hồi tăng lên, cảm giác ứng dụng bị chậm dần. Và thực sự đúng là như vậy:

1 million documents

Ảnh minh họa: Khi collection có 1 triệu documents

.ub2ca91b2c80b9a820734bae74491b44e { padding:0px; margin: 0; padding-top:1em!important; padding-bottom:1em!important; width:100%; display: block; font-weight:bold; background-color:#eaeaea; border:0!important; border-left:4px solid #34495E!important; text-decoration:none; } .ub2ca91b2c80b9a820734bae74491b44e:active, .ub2ca91b2c80b9a820734bae74491b44e:hover { opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; text-decoration:none; } .ub2ca91b2c80b9a820734bae74491b44e { transition: background-color 250ms; webkit-transition: background-color 250ms; opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; } .ub2ca91b2c80b9a820734bae74491b44e .ctaText { font-weight:bold; color:inherit; text-decoration:none; font-size: 16px; } .ub2ca91b2c80b9a820734bae74491b44e .postTitle { color:#000000; text-decoration: underline!important; font-size: 16px; } .ub2ca91b2c80b9a820734bae74491b44e:hover .postTitle { text-decoration: underline!important; }

  Hiệu năng, lập trình hàm cho collections trong Swift

.ua83f70e71c1e205cb71df96e2de96e73 { padding:0px; margin: 0; padding-top:1em!important; padding-bottom:1em!important; width:100%; display: block; font-weight:bold; background-color:#eaeaea; border:0!important; border-left:4px solid #34495E!important; text-decoration:none; } .ua83f70e71c1e205cb71df96e2de96e73:active, .ua83f70e71c1e205cb71df96e2de96e73:hover { opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; text-decoration:none; } .ua83f70e71c1e205cb71df96e2de96e73 { transition: background-color 250ms; webkit-transition: background-color 250ms; opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; } .ua83f70e71c1e205cb71df96e2de96e73 .ctaText { font-weight:bold; color:inherit; text-decoration:none; font-size: 16px; } .ua83f70e71c1e205cb71df96e2de96e73 .postTitle { color:#000000; text-decoration: underline!important; font-size: 16px; } .ua83f70e71c1e205cb71df96e2de96e73:hover .postTitle { text-decoration: underline!important; }

  Ngăn ngừa lãng phí bộ nhớ trong Java Collections như thế nào?

Ảnh minh họa: Khi collection đạt tới 2 triệu documents

Trời ơi tin được không, một request mà mất tới 16s mới có response. Thật không thể tin được. Vậy nên bài này mình sẽ viết và cùng các bạn tìm hiểu và resolve issue trên nhé.

Mức độ ảnh hưởng

Như vậy, nó sẽ ảnh hưởng như nào tới hệ thống của chúng ta nhỉ?

  • Gây ảnh hưởng trực tiếp tới trải nghiệm người dùng khi sử dụng chức năng có dùng trực tiếp API trên.
  • Ngoài ra, cũng gián tiếp ảnh hưởng tới các service khác dùng chúng vì có time khá lớn.
  • Nghiêm trọng hơn, nếu số lượng documents tăng lên 3 triệu, 5 triệu, 10 triệu thì không bao lâu, cái API kia thậm chí sẽ quá timeout và không có response luôn. Khi bị gọi quá nhiều, memory cũng sẽ tăng lên cao và số lượng request được xử lý bị giảm đi. Khi tới cực đại, sẽ có các request bị “từ chối” vì quá tải.

Nguyên nhân

Trước tiên, chúng ta cùng nhìn lại đoạn code cũ xử lý việc lấy danh sách posts ra nha:

Trong bài viết trước, đoạn code API lấy danh sách documents trong collection postsđưa ra khá ngắn gọn và đơn giản. Chỉ sử dụng một method paginate cung cấp bởi plugin cho mongoose là mongoose-paginate-v2. Như vậy, issue này rất có thể do logic phân trang bên trong plugin. Cũng không ngoại trừ khả năng là do collection này được khai báo và có vấn đề gì đó, nên khi query data ra bị chậm.

Tìm hiểu và khắc phục

Để xen vấn đề do đâu, chúng ta hãy thử cách khác để lấy dữ liệu cho một trang nhé. Dùng cách truyền thống với find và limit như sau coi tốc độ query ra sao:

Tốc độ truy vấn rất nhanh, chưa đầy 30ms đã có response về. Bây giờ, có thể khẳng định là do việc phân trang với plugin trên có vấn đề. Cách đơn giản nhất là chúng ta tự thực hiện phân trang luôn mà không cần dùng tới plugin để ứng dụng chạy trơn chu trở lại. Ngoài ra, cũng có thể tạo issue và pull request để fix lỗi cho plugin trên.

Mình sẽ viết thực hiện phân trang luôn nhé. Có một số cách hữu hiệu để phân trang nhanh collection như sau:

C1: Sử dụng find, skip và limit

Sử dụng thuật toàn phân trang với skip và limit. Trong đó:

  • limit để lấy đúng số lượng documents trong một trang
  • skip để bỏ qua số documents ở các trang trước trang hiện tại.

Lúc này, đoạn code sẽ trở thành như sau:

Ở đoạn code trên có thực hiện thêm một truy vấn để đếm tổng số documents. Nếu không cần thiết bạn có thể bỏ truy vấn này cũng được.

C2: Đơn giản với find và limit

Một cách khác, chúng ta không dựa vào số trang hiện tại nữa. Mà sẽ dùng id của document cuối cùng trong lần request trước đó. Logic sẽ hiểu kiểu như này: “Lấy 20 documents tiếp theo sau document có id là xxx”.

Cách triển khai như sau:

Tổng kết

Sau khi áp dụng một trong 2 cách trên, request sẽ có response trong ~40ms. Nhanh như lúc mới đầu. Tới đây chúng ta đã resolve được issue về thời gian phản hồi của request trong part 1. Rất cảm ơn các bạn đã quan tâm và đón đọc bài viết của mình!

.u6e1ba62b38ca6614fc5dcc8c86688baf { padding:0px; margin: 0; padding-top:1em!important; padding-bottom:1em!important; width:100%; display: block; font-weight:bold; background-color:#eaeaea; border:0!important; border-left:4px solid #34495E!important; text-decoration:none; } .u6e1ba62b38ca6614fc5dcc8c86688baf:active, .u6e1ba62b38ca6614fc5dcc8c86688baf:hover { opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; text-decoration:none; } .u6e1ba62b38ca6614fc5dcc8c86688baf { transition: background-color 250ms; webkit-transition: background-color 250ms; opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; } .u6e1ba62b38ca6614fc5dcc8c86688baf .ctaText { font-weight:bold; color:inherit; text-decoration:none; font-size: 16px; } .u6e1ba62b38ca6614fc5dcc8c86688baf .postTitle { color:#000000; text-decoration: underline!important; font-size: 16px; } .u6e1ba62b38ca6614fc5dcc8c86688baf:hover .postTitle { text-decoration: underline!important; }

  Tạo API server trên Node.js với Express và MongoDB (Part 1)

.u09116069b3663c0d3ef155f2ea32dceb { padding:0px; margin: 0; padding-top:1em!important; padding-bottom:1em!important; width:100%; display: block; font-weight:bold; background-color:#eaeaea; border:0!important; border-left:4px solid #34495E!important; text-decoration:none; } .u09116069b3663c0d3ef155f2ea32dceb:active, .u09116069b3663c0d3ef155f2ea32dceb:hover { opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; text-decoration:none; } .u09116069b3663c0d3ef155f2ea32dceb { transition: background-color 250ms; webkit-transition: background-color 250ms; opacity: 1; transition: opacity 250ms; webkit-transition: opacity 250ms; } .u09116069b3663c0d3ef155f2ea32dceb .ctaText { font-weight:bold; color:inherit; text-decoration:none; font-size: 16px; } .u09116069b3663c0d3ef155f2ea32dceb .postTitle { color:#000000; text-decoration: underline!important; font-size: 16px; } .u09116069b3663c0d3ef155f2ea32dceb:hover .postTitle { text-decoration: underline!important; }

  Xây dựng API login Google, Facebook, Twitter hay Github với server laravel cho các dự án về App Mobile

Chia sẻ bài viết ngay

Nguồn bài viết : viblo