Với bất kỳ cơ sở dữ liệu nào, bao gồm MongoDB, chỉ mục là một phần quan trọng trong quá trình phát triển. Không có chỉ mục, khi dữ liệu của bạn tăng lên, hiệu năng cơ sở dữ liệu sẽ giảm theo. Với các chỉ mục phù hợp, khi dữ liệu tăng, hiệu năng truy cập vẫn sẽ được tối ưu.
Trong bài viết này, chúng ta sẽ khám phá các loại chỉ mục khác nhau có trong MongoDB, một số mẹo và thủ thuật để có trải nghiệm tốt nhất, cũng như hướng dẫn để đảm bảo bạn thật sự tận dụng được các chỉ mục mình tạo. Nếu bạn vẫn đang học MongoDB, tôi khuyến nghị xem qua khóa học Giới thiệu về MongoDB.
Chỉ mục MongoDB quan trọng ở đâu và vì sao?
Khi nói đến chỉ mục, là nói đến hiệu năng. Chỉ mục quyết định việc phải xem từng tài liệu trong một collection để xem có khớp truy vấn hay không, hay chỉ xem một lô tài liệu cụ thể phù hợp với truy vấn. Nếu collection nhỏ và không có nhiều tài liệu để quét, hiệu năng sẽ tốt trong cả hai trường hợp. Nhưng nếu chúng ta có 100.000 tài liệu trong một collection và 100 tài liệu sẽ khớp truy vấn, sẽ mất kha khá thời gian để xem hết 100.000 tài liệu để tìm 100 tài liệu đó nếu không có chỉ mục dựa trên các trường liên quan.
Có nhiều loại chỉ mục trong MongoDB:
Trường đơn: Chỉ mục trên một trường cụ thể trong tài liệu. Chỉ mục mặc định của trường _id có thể coi là chỉ mục trường đơn.
Tổng hợp (compound): Chỉ mục dựa trên nhiều trường của một tài liệu, trong đó thứ tự các trường dùng trong truy vấn và trong chỉ mục là quan trọng.
Multikey: Chỉ mục dùng cho các trường dạng mảng.
Wildcard: Chỉ mục hỗ trợ các trường có thể được đặt tên hơi khác nhau giữa các tài liệu. Loại chỉ mục này sẽ không hiệu quả bằng các chỉ mục khác.
Không gian địa lý: Chỉ mục để cải thiện hiệu năng cho các truy vấn không gian địa lý sử dụng dữ liệu tọa độ vĩ độ và kinh độ.
Băm (hashed): Chỉ mục phù hợp nhất khi dùng với khóa phân mảnh (shard key).
Thường thì bạn sẽ dùng các chỉ mục trường đơn, compound, hoặc multikey, nhưng cuối cùng vẫn phụ thuộc vào loại ứng dụng, truy vấn và dữ liệu.
Chỉ mục quan trọng cho mọi thao tác với cơ sở dữ liệu có truy xuất tài liệu. Điều này không chỉ giới hạn ở việc lấy tài liệu bằng thao tác find hoặc findOne. Khi cập nhật và xóa tài liệu, bạn cũng sử dụng tiêu chí khớp mà cơ sở dữ liệu dùng để tìm tài liệu trước khi thực hiện cập nhật hoặc xóa.
Các toán tử find và findOne không phải là những toán tử truy xuất duy nhất được hưởng lợi từ chỉ mục. Khi dùng toán tử $match làm công đoạn đầu tiên trong pipeline aggregation, chỉ mục sẽ tăng tốc độ đáng kể.
Mẹo và thủ thuật lập chỉ mục MongoDB để thành công
Tạo chỉ mục ngẫu nhiên có thể kém hiệu quả chẳng khác gì không dùng chỉ mục trong MongoDB. Thay vào đó, có vài điều cần cân nhắc để giúp bạn xây dựng ứng dụng hiệu quả hơn nhiều.
Equality, sort, range (ESR) cho chỉ mục compound
Quy tắc ngón tay cái khi tạo chỉ mục compound là xác định bạn sẽ dùng từng trường trong truy vấn như thế nào và đảm bảo tuân theo thực hành tốt ESR.
ESR nghĩa là gì? Cùng phân tích:
Equality (bằng đúng): Các trường sẽ được khớp chính xác, không khớp lỏng. Ví dụ, khớp chính xác tên của một người trong truy vấn sẽ là giai đoạn equality.
Sort (sắp xếp): Các trường bạn dùng để sắp xếp theo thứ tự tăng dần hoặc giảm dần.
Range (phạm vi): Các trường bạn sẽ khớp nhưng không phải khớp chính xác. Ví dụ, dùng toán tử $lt hoặc $gt sẽ khớp các tài liệu trong một phạm vi, chứ không phải một giá trị cụ thể.
Để hình dung rõ hơn, hãy xem truy vấn sau:
db.employee.find({"department":"Developer Relations","salary":{"$lt":80000}}).sort({"name":1});Được hỗ trợ bởi
Truy vấn trên thực hiện khớp equality trên phòng ban, khớp theo phạm vi trên lương, và sắp xếp kết quả theo tên.
Theo quy tắc ESR, chỉ mục sẽ trông như sau:
db.employee.createIndex({"department":1,"name":1,"salary":1});Được hỗ trợ bởi
Chúng ta muốn tuân theo quy tắc ESR để tránh sắp xếp trong bộ nhớ và thu hẹp không gian tìm kiếm cho việc thực thi truy vấn hiệu quả.
Truy vấn được phủ (covered queries)
Nói chung, lập chỉ mục sẽ tạo một tập con nhỏ hơn các tài liệu để quét khi chạy truy vấn và nhờ đó cải thiện hiệu năng. Nhưng nếu bạn muốn đẩy hiệu năng ứng dụng lên cấp độ tiếp theo, hãy khám phá covered queries. Với covered query, bạn lấy toàn bộ kết quả truy vấn trực tiếp từ chỉ mục, không phải quét qua bất kỳ tài liệu nào trong collection.
Có một vài điều kiện có thể khiến covered query khả thi hoặc không trong ứng dụng của bạn:
Tiêu chí khớp và sắp xếp phải khớp chính xác với những gì chỉ mục của bạn được tạo ra xung quanh.
Các trường bạn yêu cầu trong tập kết quả cũng phải có trong chỉ mục.
Khi sharding, shard key cần là một phần của chỉ mục.
Vậy tất cả điều này có nghĩa là gì?
Lấy ví dụ chỉ mục sau:
db.department.createIndex({"country":1,"salary":1});Được hỗ trợ bởi
Trong ví dụ trên, chúng ta muốn lập chỉ mục tài liệu theo trường country và trường salary. Sau đó, có thể chạy truy vấn sau:
db.department.find({"country":"US"},{"country":1,"salary":1});Được hỗ trợ bởi
Truy vấn trên, có kèm projection, sẽ dùng chỉ mục của chúng ta, nhưng không phải covered query. Trường _id được trả về mặc định trong MongoDB và trong ví dụ này, trường _id chưa được thêm vào chỉ mục. Vậy chúng ta có thể đổi chỉ mục hoặc đổi truy vấn. Nếu đổi truy vấn, có thể làm như sau:
db.department.find({"country":"US"},{"country":1,"salary":1,"_id":0});Được hỗ trợ bởi
Hãy nhớ, nếu bạn muốn dùng covered query, chỉ mục phải hợp lệ và các trường được yêu cầu phải có trong chỉ mục. Nếu không, bạn chỉ đang tìm tài liệu được chỉ mục quản lý, chứ không phải ngay trong chỉ mục.
Bạn có thể kiểm tra một truy vấn có được phủ hay không bằng cách dùng đối số executionStats trong toán tử explain được đề cập ở phần sau của bài viết. Kết quả totalDocsExamined sẽ cho biết đang dùng gì. Nếu giá trị là 0, bạn đang dùng covered query.
Giảm hoặc loại bỏ các chỉ mục không cần thiết
Chỉ mục không “miễn phí” về mặt tài nguyên máy chủ. Mỗi chỉ mục cần được máy chủ duy trì và việc này tiêu tốn CPU, RAM, và đĩa. Khi bạn tạo thêm nhiều chỉ mục, bạn sẽ tạo thêm overhead, có thể dẫn đến chi phí cao hơn hoặc giảm hiệu năng nếu máy chủ được cấp phát thiếu. Sự suy giảm hiệu năng này có thể ảnh hưởng đến ghi và xóa, không chỉ đọc, vì MongoDB phải tiêu thụ nhiều tài nguyên hơn.
Chỉ nên tạo chỉ mục cho các truy vấn tần suất cao. Truy vấn chạy mỗi tháng một lần có thật sự cần chỉ mục không? Có lẽ không. Truy vấn chạy mỗi 10 giây có cần chỉ mục không? Chắc chắn rồi!
Tìm hiểu thêm vài thực hành tốt về lập chỉ mục trên Blog MongoDB.
Chỉ mục MongoDB của bạn có hoạt động không?
Chỉ vì bạn đã tạo chỉ mục trong MongoDB không có nghĩa là truy vấn bạn chạy thực sự đang dùng nó. Trong MongoDB, chúng ta có thể kiểm tra chỉ mục nào đang được dùng cho một truy vấn bất kỳ.
Hãy lấy ví dụ chỉ mục sau:
db.people.createIndex({"name":1,"is_active":1});Được hỗ trợ bởi
Trong ví dụ trên, chúng ta lập chỉ mục trên trường name và trường is_active. Bây giờ, ta muốn chạy truy vấn sau:
Truy vấn trên sẽ chạy dù có hay không có chỉ mục hợp lệ, và nếu bộ dữ liệu nhỏ, nó sẽ chạy nhanh dù thế nào. Tuy nhiên, nếu muốn xem nó có dùng chỉ mục của chúng ta hay không, ta có thể điều chỉnh truy vấn như sau:
db.people.find({"is_active":true}).explain();Được hỗ trợ bởi
Lần này, chúng ta dùng phương thức explain để lấy kế hoạch thực thi của truy vấn. Trong kế hoạch thực thi, có thể thấy như sau:
Với truy vấn này, MongoDB dùng "COLLSCAN", nghĩa là nó không sử dụng chỉ mục của chúng ta dù truy vấn có chứa một trong các trường của chỉ mục. Nếu chúng ta đổi truy vấn thành như sau:
db.people.find({"name":"Nic Raboy","is_active":true}).explain();Được hỗ trợ bởi
Lần này, kế hoạch thực thi cho biết chúng ta đã dùng "IXSCAN" và chỉ rõ tên chỉ mục được dùng, ở đây là chỉ mục name_1_is_active_1. Tên được tự động đặt khi chúng ta tạo chỉ mục.
Với thao tác update, quy trình tương tự, ngoại trừ vị trí đặt hàm explain.
Một thao tác update có thể trông như sau:
db.people.explain().update({“name”: “Nic Raboy”,“Is_active”:true},{
“$set”:{“is_active”:false}});Được hỗ trợ bởi
Lưu ý trong ví dụ trên, explain được gọi trước hàm update.
Một cách khác để tìm chi tiết kế hoạch thực thi truy vấn là dùng runCommand trên cơ sở dữ liệu. Dù không đẹp mắt, nhưng rất mạnh mẽ và trông như sau:
db.runCommand({"explain":{"update":"people","updates":[{"q":{"name":"Nic Raboy","is_active":true},"u":{"$set":{"is_active":false}}}]},"verbosity":"allPlansExecution"});Được hỗ trợ bởi
Đoạn trên hoạt động tương tự như thao tác cập nhật với hàm explain.
Nếu bạn chưa tự tin khi tạo chỉ mục, hoặc nhận thấy truy vấn chạy chậm hơn mong đợi, hãy kiểm tra chi tiết thực thi.
Kết luận
Bạn vừa xem một số thực hành tốt quanh việc lập chỉ mục trong MongoDB. Dù chúng ta không khám phá mọi kịch bản lập chỉ mục có thể có, bạn có thể dùng đây làm điểm khởi đầu để đảm bảo người dùng có trải nghiệm tốt nhất khi làm việc với dữ liệu của bạn.
Nhắc lại một vài điểm:
Tuân theo quy tắc equality, sort, range (ESR) bất cứ khi nào có thể.
Nếu có thể, hãy cố gắng thực hiện covered query.
Đừng tạo chỉ mục tràn lan; thay vào đó hãy xác định truy vấn nào được dùng nhiều nhất và lượng dữ liệu được lấy trong đó.
Dựa vào toán tử explain để kiểm thử chỉ mục và hiệu năng của chúng.
Các điểm trên không phải là danh sách đầy đủ các thực hành tốt về lập chỉ mục để cải thiện hiệu năng ứng dụng. Còn những yếu tố khác, chẳng hạn như mức độ phân biệt (cardinality).
Để được hỗ trợ thêm về lập chỉ mục, hãy xem tài liệu MongoDB. Nếu bạn dùng gói trả phí trong MongoDB Atlas, hãy cân nhắc sử dụng các công cụ tư vấn hiệu năng tích hợp sẵn để gợi ý chỉ mục và truy vấn.
Câu hỏi thường gặp về lập chỉ mục MongoDB
Tôi nên tạo bao nhiêu chỉ mục trong một collection MongoDB?⌄
Còn tùy. Bạn cần tìm điểm cân bằng phù hợp với mình, nhưng hãy cân nhắc chỉ lập chỉ mục cho các truy vấn được thực thi thường xuyên, không phải thỉnh thoảng.
Tôi đã tạo vài chỉ mục, nhưng truy vấn vẫn chậm. Tại sao?⌄
Bạn có chắc truy vấn của mình thực sự đang dùng các chỉ mục đã tạo không? Hãy dùng toán tử explain hoặc một số công cụ cố vấn trong MongoDB Atlas để khắc phục sự cố.
Với quy tắc ESR, thứ tự trường trong chỉ mục có phải chính xác là equality, rồi sort, rồi range không?⌄
Dù MongoDB đôi khi có thể dùng chỉ mục sai thứ tự, hiệu quả nhất là khi thứ tự khóa chỉ mục khớp với trình tự truy vấn: trước hết là các bộ lọc equality, sau đó sắp xếp, và cuối cùng là phạm vi.
Các chế độ explain khác nhau là gì? ⌄
Mặc định, explain() chạy ở chế độ “queryPlanner”, nhưng nếu muốn các chế độ khác, cũng có “executionStats” và “allPlansExecution”.
Tác giả
Nic Raboy
Nic Raboy là Trưởng bộ phận Quan hệ Nhà phát triển tại MongoDB, nơi anh dẫn dắt một đội ngũ các nhà phát triển Python, Java, C# và PHP tạo ra nội dung xuất sắc giúp các nhà phát triển triển khai MongoDB thành công trong dự án của mình. Anh có kinh nghiệm với Golang và JavaScript, và thường xuyên viết về nhiều hành trình phát triển của bản thân.
Với bất kỳ cơ sở dữ liệu nào, bao gồm MongoDB, chỉ mục là một phần quan trọng trong quá trình phát triển. Không có chỉ mục, khi dữ liệu của bạn tăng lên, hiệu năng cơ sở dữ liệu sẽ giảm theo. Với các chỉ mục phù hợp, khi dữ liệu tăng, hiệu năng truy cập vẫn sẽ được tối ưu.
Trong bài viết này, chúng ta sẽ khám phá các loại chỉ mục khác nhau có trong MongoDB, một số mẹo và thủ thuật để có trải nghiệm tốt nhất, cũng như hướng dẫn để đảm bảo bạn thật sự tận dụng được các chỉ mục mình tạo. Nếu bạn vẫn đang học MongoDB, tôi khuyến nghị xem qua khóa học Giới thiệu về MongoDB.
Chỉ mục MongoDB quan trọng ở đâu và vì sao?
Khi nói đến chỉ mục, là nói đến hiệu năng. Chỉ mục quyết định việc phải xem từng tài liệu trong một collection để xem có khớp truy vấn hay không, hay chỉ xem một lô tài liệu cụ thể phù hợp với truy vấn. Nếu collection nhỏ và không có nhiều tài liệu để quét, hiệu năng sẽ tốt trong cả hai trường hợp. Nhưng nếu chúng ta có 100.000 tài liệu trong một collection và 100 tài liệu sẽ khớp truy vấn, sẽ mất kha khá thời gian để xem hết 100.000 tài liệu để tìm 100 tài liệu đó nếu không có chỉ mục dựa trên các trường liên quan.
Có nhiều loại chỉ mục trong MongoDB:
_idcó thể coi là chỉ mục trường đơn.Để biết thêm về các loại chỉ mục, hãy xem tài liệu MongoDB.
Thường thì bạn sẽ dùng các chỉ mục trường đơn, compound, hoặc multikey, nhưng cuối cùng vẫn phụ thuộc vào loại ứng dụng, truy vấn và dữ liệu.
Chỉ mục quan trọng cho mọi thao tác với cơ sở dữ liệu có truy xuất tài liệu. Điều này không chỉ giới hạn ở việc lấy tài liệu bằng thao tác
findhoặcfindOne. Khi cập nhật và xóa tài liệu, bạn cũng sử dụng tiêu chí khớp mà cơ sở dữ liệu dùng để tìm tài liệu trước khi thực hiện cập nhật hoặc xóa.Các toán tử
findvàfindOnekhông phải là những toán tử truy xuất duy nhất được hưởng lợi từ chỉ mục. Khi dùng toán tử$matchlàm công đoạn đầu tiên trong pipeline aggregation, chỉ mục sẽ tăng tốc độ đáng kể.Mẹo và thủ thuật lập chỉ mục MongoDB để thành công
Tạo chỉ mục ngẫu nhiên có thể kém hiệu quả chẳng khác gì không dùng chỉ mục trong MongoDB. Thay vào đó, có vài điều cần cân nhắc để giúp bạn xây dựng ứng dụng hiệu quả hơn nhiều.
Equality, sort, range (ESR) cho chỉ mục compound
Quy tắc ngón tay cái khi tạo chỉ mục compound là xác định bạn sẽ dùng từng trường trong truy vấn như thế nào và đảm bảo tuân theo thực hành tốt ESR.
ESR nghĩa là gì? Cùng phân tích:
$lthoặc$gtsẽ khớp các tài liệu trong một phạm vi, chứ không phải một giá trị cụ thể.Để hình dung rõ hơn, hãy xem truy vấn sau:
Truy vấn trên thực hiện khớp equality trên phòng ban, khớp theo phạm vi trên lương, và sắp xếp kết quả theo tên.
Theo quy tắc ESR, chỉ mục sẽ trông như sau:
Chúng ta muốn tuân theo quy tắc ESR để tránh sắp xếp trong bộ nhớ và thu hẹp không gian tìm kiếm cho việc thực thi truy vấn hiệu quả.
Truy vấn được phủ (covered queries)
Nói chung, lập chỉ mục sẽ tạo một tập con nhỏ hơn các tài liệu để quét khi chạy truy vấn và nhờ đó cải thiện hiệu năng. Nhưng nếu bạn muốn đẩy hiệu năng ứng dụng lên cấp độ tiếp theo, hãy khám phá covered queries. Với covered query, bạn lấy toàn bộ kết quả truy vấn trực tiếp từ chỉ mục, không phải quét qua bất kỳ tài liệu nào trong collection.
Có một vài điều kiện có thể khiến covered query khả thi hoặc không trong ứng dụng của bạn:
Vậy tất cả điều này có nghĩa là gì?
Lấy ví dụ chỉ mục sau:
Trong ví dụ trên, chúng ta muốn lập chỉ mục tài liệu theo trường
countryvà trườngsalary. Sau đó, có thể chạy truy vấn sau:Truy vấn trên, có kèm projection, sẽ dùng chỉ mục của chúng ta, nhưng không phải covered query. Trường
_idđược trả về mặc định trong MongoDB và trong ví dụ này, trường_idchưa được thêm vào chỉ mục. Vậy chúng ta có thể đổi chỉ mục hoặc đổi truy vấn. Nếu đổi truy vấn, có thể làm như sau:Hãy nhớ, nếu bạn muốn dùng covered query, chỉ mục phải hợp lệ và các trường được yêu cầu phải có trong chỉ mục. Nếu không, bạn chỉ đang tìm tài liệu được chỉ mục quản lý, chứ không phải ngay trong chỉ mục.
Bạn có thể kiểm tra một truy vấn có được phủ hay không bằng cách dùng đối số
executionStatstrong toán tửexplainđược đề cập ở phần sau của bài viết. Kết quảtotalDocsExaminedsẽ cho biết đang dùng gì. Nếu giá trị là 0, bạn đang dùng covered query.Giảm hoặc loại bỏ các chỉ mục không cần thiết
Chỉ mục không “miễn phí” về mặt tài nguyên máy chủ. Mỗi chỉ mục cần được máy chủ duy trì và việc này tiêu tốn CPU, RAM, và đĩa. Khi bạn tạo thêm nhiều chỉ mục, bạn sẽ tạo thêm overhead, có thể dẫn đến chi phí cao hơn hoặc giảm hiệu năng nếu máy chủ được cấp phát thiếu. Sự suy giảm hiệu năng này có thể ảnh hưởng đến ghi và xóa, không chỉ đọc, vì MongoDB phải tiêu thụ nhiều tài nguyên hơn.
Chỉ nên tạo chỉ mục cho các truy vấn tần suất cao. Truy vấn chạy mỗi tháng một lần có thật sự cần chỉ mục không? Có lẽ không. Truy vấn chạy mỗi 10 giây có cần chỉ mục không? Chắc chắn rồi!
Tìm hiểu thêm vài thực hành tốt về lập chỉ mục trên Blog MongoDB.
Chỉ mục MongoDB của bạn có hoạt động không?
Chỉ vì bạn đã tạo chỉ mục trong MongoDB không có nghĩa là truy vấn bạn chạy thực sự đang dùng nó. Trong MongoDB, chúng ta có thể kiểm tra chỉ mục nào đang được dùng cho một truy vấn bất kỳ.
Hãy lấy ví dụ chỉ mục sau:
Trong ví dụ trên, chúng ta lập chỉ mục trên trường
namevà trườngis_active. Bây giờ, ta muốn chạy truy vấn sau:Truy vấn trên sẽ chạy dù có hay không có chỉ mục hợp lệ, và nếu bộ dữ liệu nhỏ, nó sẽ chạy nhanh dù thế nào. Tuy nhiên, nếu muốn xem nó có dùng chỉ mục của chúng ta hay không, ta có thể điều chỉnh truy vấn như sau:
Lần này, chúng ta dùng phương thức
explainđể lấy kế hoạch thực thi của truy vấn. Trong kế hoạch thực thi, có thể thấy như sau:Với truy vấn này, MongoDB dùng "COLLSCAN", nghĩa là nó không sử dụng chỉ mục của chúng ta dù truy vấn có chứa một trong các trường của chỉ mục. Nếu chúng ta đổi truy vấn thành như sau:
Kết quả
explainđã thay đổi:Lần này, kế hoạch thực thi cho biết chúng ta đã dùng "IXSCAN" và chỉ rõ tên chỉ mục được dùng, ở đây là chỉ mục
name_1_is_active_1. Tên được tự động đặt khi chúng ta tạo chỉ mục.Với thao tác
update, quy trình tương tự, ngoại trừ vị trí đặt hàmexplain.Một thao tác
updatecó thể trông như sau:Lưu ý trong ví dụ trên,
explainđược gọi trước hàmupdate.Nếu bạn đang dùng đúng chỉ mục trong thao tác cập nhật, bạn sẽ thấy giá trị "IXSCAN" cùng tên chỉ mục. Tìm hiểu thêm về các thao tác cập nhật và cách dùng chỉ mục với chúng.
Một cách khác để tìm chi tiết kế hoạch thực thi truy vấn là dùng
runCommandtrên cơ sở dữ liệu. Dù không đẹp mắt, nhưng rất mạnh mẽ và trông như sau:Đoạn trên hoạt động tương tự như thao tác cập nhật với hàm
explain.Nếu bạn chưa tự tin khi tạo chỉ mục, hoặc nhận thấy truy vấn chạy chậm hơn mong đợi, hãy kiểm tra chi tiết thực thi.
Kết luận
Bạn vừa xem một số thực hành tốt quanh việc lập chỉ mục trong MongoDB. Dù chúng ta không khám phá mọi kịch bản lập chỉ mục có thể có, bạn có thể dùng đây làm điểm khởi đầu để đảm bảo người dùng có trải nghiệm tốt nhất khi làm việc với dữ liệu của bạn.
Nhắc lại một vài điểm:
explainđể kiểm thử chỉ mục và hiệu năng của chúng.Các điểm trên không phải là danh sách đầy đủ các thực hành tốt về lập chỉ mục để cải thiện hiệu năng ứng dụng. Còn những yếu tố khác, chẳng hạn như mức độ phân biệt (cardinality).
Để được hỗ trợ thêm về lập chỉ mục, hãy xem tài liệu MongoDB. Nếu bạn dùng gói trả phí trong MongoDB Atlas, hãy cân nhắc sử dụng các công cụ tư vấn hiệu năng tích hợp sẵn để gợi ý chỉ mục và truy vấn.