Thứ Năm, 15 tháng 3, 2018

MongoDB Best Practice

MongoDB có rất nhiều nhược điểm và không phải DB mạnh về Performance. Tuy nhiên nó lại là DB quá dễ dành để sử dụng, vận hành và phát triển nên được sử dụng rất rộng rãi. Sau gần 5 năm sử dụng mình có tổng kết được một số thứ cần chú ý khi làm việc với MongoDB.

MongoDB logs




Memset của MongoDB

MongoDB sử dụng Memmory để cache data rất nhiều đặt biệt là dùng cho Index. Mặc định MongoDB sẽ sử dụng 50% lượng RAM của Server để làm không gian làm việc (Số này có thể thay đổi Configuration để tăng lên thêm). MongoDB sử dụng tối đa 80% lượng RAM giới hạn đó cho phần cache, 20% sử dụng để thao tác DB (query, update…). Nghĩa là 1 server 32GB RAM thì mặc định chỉ sử dụng được 16GB RAM cho MongoDB, trong đó khoảng 13GB có thể dụng để Caching. Trong điều kiện có đủ RAM thì MongoDB sẽ load toàn bộ Index vào RAM để làm việc. Thế nên tốt nhất các bạn nên giữ lượng Index sử dụng cho toàn bộ DB nhỏ hơn con số 80% kia. Với nguyên tăc trên thì phần Index sẽ được sử dụng hiệu quả nhất. Thế nên bạn không nên sử dụng Index một cách bừa bãi.

Cơ chế thao tác Query với Index

Việc Index các field trong MongoDB có tác dụng giúp nhanh chóng cho 2 việc: Lookup (Query) và Sort (Chú ý thời gian để Sort chậm hơn rất nhiều so với Query nếu không có Index). Vậy điều gì sẽ xảy ra nếu Index không có tác dụng với Query? Không giống như những DB có cấu trúc, MongoDB là dữ liệu phi cấu trúc. Do vậy MongoDB đọc toàn bộ Data của từng Document ra để đọc kiểm tra (Đối với những DB có cấu trúc DB chỉ cần đọc những trường cần kiểm tra thôi còn những trường không cần thiết thì không cần). Nếu dữ liệu của mỗi Document lớn thì thời gian để đọc DB ra sẽ rất lớn. Thế nên các bạn cần đặc biệt để ý khi query với những Collection lớn. Trong trường hợp điều kiện Query là: Fiedl1=A1, Field2=B1 mà ta chỉ Index cho Fiedl1 thì điều gì sẽ xảy ra. MongoDB sẽ đọc dữ liệu Index để tìm ra tất cả những Document có Fiedl1=A1. Sau đó MongoDB sẽ đọc toàn bộ Document này và kiểm tra dữ liệu của Field2, nếu Document nào thoả mãn thì sẽ đưa vào kết quả trả về. Nếu có Index thì việc kiểm tra điều kiện Query và sắp xếp chỉ cần thực hiện trên dữ liệu Index chứ không cần thực hiện trên dữ liệu của cả Document (nếu dữ liệu Index lưu hoàn toàn trên RAM thì việc thực hiện Query, Sort sẽ rất nhanh. Nghĩa là Index đúng và không đúng có thể khiến cho thời gian xử lý chênh nhau đến cả ngàn lần.

Nguyên tắc đánh Index của MongoDB

Như vậy nhiều bạn sẽ nghĩ ra ta chỉ cần Index tất cả các trường cần Query / Sort là xong. Nhưng thực tế lại không đúng như vậy. MongoDB hầu như không kết hợp được 2 Index khác nhau trong 1 Query. Điều đó có nghĩa khi ta Query với điệu kiện Fiedl1=A1, Field2=B1, thì việc tạo 2 Index cho Fiedl1 và Field2 sẽ không có tác dụng (Gần như chỉ có thể sử dụng được 1 trong 2 Index). Vậy Index đúng trong trường hợp này là gì: ta cần 1 Index cho cả 2 Fiedl1 và Field2 (Compound Index). Vậy ta lại nghĩ thôi tạo Index với tất cả các Fiedl trong Collection là xong? Thực tế lại không như vậy. Ví dụ ta Tạo Index cho cặp: Fiedl1, Field2, Field3, Field4 (Chú ý thứ tự các trường của Index rất có ý nghĩa) Thì Index ta chỉ có thể sử dụng cho các điều kiện: Fiedl1=X1 Field2=X2 Field3=X3 Field4=X4, Fiedl1=X1 Field2=X2 Field3=X3, Fiedl1=X1 Field2=X2, Fiedl1=X1. Nghĩa là Index này không có tác dụng khi ta Query với điều kiện khác nhau Field2=X2, Field3=X3, Field3=X3 Field4=X4… (thực tế là MongoDB có tác dụng một chút xíu khi query với những điều kiện trên nhưng rất ít hiệu quả). Như vậy ta cần tạo ra những Index để phù hợp với Query cần có kiểm tra code và Slow Query log (Xem phần sau). Ngoài ra 1 điểm cần chú ý nữa nguyên tác sử dụng Index trên chỉ dành cho điều kiện bằng (Equal) hoặc điều kiện $in. Những điều kiện khác như lớn hơn, nhỏ hơn có thể sử dụng Index nhưng nếu sử dụng điều kiện đó thì những field tiếp theo không sử dụng được Index. Ví dụ ta có với Index như trên (Fiedl1 Field2 Field3 Field4). Nếu ta sử dụng điều kiện: Fiedl1=X1 Field2>X2 Field3=X3, thì Index trên chỉ sử dụng được cho 2 trường Fiedl1 và Field2. Với điều kiện Field3=X3 phải kiểm tra bằng cách đọc dữ liệu của những Document thoả mãn điều kiện Fiedl1=X1 Field2>X2.

Aggregation, Map Reduce, Text Search:

Về nguyên tắc MongoDB hỗ trợ Aggregation, Map/Reduce, Text Search. Tuy nhiên những Function này rất kém hiệu quả. Nếu bạn cần thực hiện việc Aggregation thì theo mình nên sử dụng việc tính toán trước thay vì việc sử dụng Aggregation. Vì Aggregation trnên MongoDB rất kém, đọc rất chậm (Có lẽ liên quan đến việc MongoDB là SchemaLess). Các bạn có thể sử dụng Replicate để tăng Performance nhưng hiệu quả cải thiện cũng không cao lắm. MongoDB cũng hỗ trợ Search bằng Regex, tuy nhiên nếu bạn thực sự muốn sử dụng Search, Fulltext Search thì mình khuyên nên sử dụng một DB khác: ví dụ như Solr hoặc ElasticSearch. Còn nếu bạn muốn sử dụng Aggregation hiệu quả trên DB thì nên sử dụng việc tính trước, nếu không thì sử dụng một DB khác thay thế (ví dụ MySQL / PosgreSQL) sẽ tốt hơn rất nhiều.

Công cụ đo lường và kiểm tra:

MongoDB thường bạn sẽ sử dụng 4 chỗ để kiểm tra Performance:

- Lệnh mongotop: Kiểm tra Collection nào đang bị chậm

- Lệnh mongostat: Kiểm tra số lượng insert/update/delete/query của MongoDB

- /var/logs/mongodb/mongodb.log: Mặc định tất cả những thao tác có thời gian xử lý lớn hơn 100ms đều được log vào đây. Bạn chỉ cần phân tích file này là có thể biết ngay được những thứ cần thay đổi trong DB. Cần thêm Index gì, thay đổi Query ra sao.

- Sử dụng Studio3T để xem các chỉ số của DB như: Số collection, số document trên từng collection, data của từng collection, dung lượng index xử dụng của từng collection…

Túm lại những thứ nên làm:

- Giới hạn tổng dung lượng Index sử dụng nhỏ hơn 80% số lượng Memset (thông thường = 40% RAM)

- Tạo Index phù hợp với Query, tạo Index theo nhóm phù hợp

- Không sử dụng những điều kiện Query: $ne / $nin / $or

- Không sử dụng Map/Reduce, Aggregation, mà thay bằng việc tính trước ra một collection khác.

- Tách riêng DB Raw và DB Report nếu có thể

- Với dữ liệu về thời gian sử dụng date với điều kiện (=), thay cho time với điều kiện trong khoảng xxxx-xx-xx 00:00:00 và xxxx-xx-xx 23:59:59

- Sử dụng điều kiện $in thay cho điều kiện nằm trong khoảng (A1 < X < A2) nếu như số giá trị là hữu hạn (Ví dụ khoảng ngày)

- Sử dụng _id mặc định của MongoDB (Dung lượng dành cho Index nhỏ hơn khá nhiều so với việc bạn tự tạo giá trị _id riêng của bạn)

- Không nên sử dụng Regex với Collection có số lượng bản ghi lớn.

- Thật cẩn thận khi sử dụng điều kiện sắp xếp. Nó có thể ảnh hưởng rất lớn đến tốc độ.

- Không Update / Delete một lượng lớn Document tại 1 thời điểm (Insert thì được, nhưng nên sử dụng insertMany)

- Sử dụng Background khi tạo Index để không ảnh hưởng đến Performance của MongoDB.

Đoàn Tuyển

Không có nhận xét nào:

Đăng nhận xét