Khắc phục giới hạn 64 chỉ mục của MongoDB: Nguyên nhân và cách xử lý lỗi 'Too Many Indexes'

intermediate🍃 MongoDB2026-03-30| MongoDB (Phiên bản 4.4 đến 7.0), Linux, Docker, MongoDB Atlas

Error Message

MongoServerError: add index fails, too many indexes for ns: mydb.mycollection
#mongodb#indexing#tối-ưu-hóa-cơ-sở-dữ-liệu#mongodb-atlas#tinh-chỉnh-hiệu-năng

Lỗi

Bạn đang đẩy một bản cập nhật quan trọng hoặc chạy migration cơ sở dữ liệu thì đột nhiên pipeline CI/CD của bạn dừng lại. Các bản log hiển thị thông báo từ chối thẳng thừng từ cơ sở dữ liệu:

MongoServerError: add index fails, too many indexes for ns: mydb.mycollection

Đây không phải là một lỗi bug hay sự cố tạm thời. Bạn đã chạm tới giới hạn cứng là 64 index cho mỗi collection. Đây là mức trần bảo vệ được tích hợp sẵn trong storage engine WiredTiger để ngăn cơ sở dữ liệu của bạn bị đình trệ.

Tại sao điều này xảy ra

Mặc dù 64 index nghe có vẻ là một con số hào phóng, nhưng các ứng dụng phức tạp thường chạm tới giới hạn này nhanh hơn dự kiến. Mỗi index bạn thêm vào đều có cái giá của nó. Mỗi index cộng thêm khoảng 10–20% chi phí (overhead) cho các thao tác ghi vì MongoDB phải cập nhật mọi cây B-tree liên quan cho mỗi lệnh insert, update, hoặc delete.

Các nguyên nhân phổ biến bao gồm:

  • Lạm dụng index trong ORM: Các công cụ như Mongoose hoặc TypeORM có thể tự động tạo index cho mọi trường được định nghĩa trong schema của bạn.
  • Dư thừa từ các tính năng cũ: Các tính năng cũ đã bị xóa, nhưng các index hỗ trợ chúng vẫn còn hoạt động và tiêu tốn tài nguyên.
  • Thói quen tạo index cho mỗi truy vấn: Tạo một index duy nhất cho mọi tổ hợp của truy vấn find() thay vì sử dụng các cấu trúc compound index (index phức hợp).

Nếu collection của bạn chứa 100 triệu document, một index dư thừa duy nhất có thể gây lãng phí 5GB RAM mà working set của bạn đang rất cần.

Khắc phục tức thì: Kiểm tra và Dọn dẹp

Bạn không thể tăng giới hạn này thông qua cấu hình. Cách duy nhất để tiếp tục là giải phóng không gian bằng cách loại bỏ những gì bạn không cần.

Bước 1: Kiểm kê các index hiện tại

Mở mongosh và liệt kê mọi index hiện đang hoạt động trên collection:

db.mycollection.getIndexes()

Hãy xem kỹ các định nghĩa key. Bạn đang tìm kiếm các phần chồng chéo nhau.

Bước 2: Loại bỏ các tiền tố (prefix) dư thừa

Các index trong MongoDB hoạt động từ trái sang phải. Nếu bạn có một compound index, bất kỳ truy vấn nào sử dụng "tiền tố" (trường đầu tiên hoặc các trường đầu tiên) đã được bao phủ. Hãy xem xét hai index sau:

  • Index A: { "tenant_id": 1 }
  • Index B: { "tenant_id": 1, "created_at": -1 }

Index A hoàn toàn dư thừa. Index B có thể xử lý bất kỳ truy vấn nào mà Index A đang đảm nhận. Việc xóa Index A sẽ ngay lập tức giải phóng một vị trí và giảm độ trễ khi ghi mà không ảnh hưởng đến hiệu suất truy vấn.

Bước 3: Loại bỏ những gánh nặng không cần thiết

Sau khi tìm thấy một index dư thừa, hãy xóa nó bằng tên:

db.mycollection.dropIndex("tenant_id_1")

Kiểm tra tiến trình của bạn bằng cách đếm số lượng index còn lại:

db.mycollection.getIndexes().length

Chiến lược giải quyết lâu dài

Nếu ứng dụng của bạn thực sự yêu cầu hơn 60 kiểu truy cập dữ liệu, bạn cần thay đổi chiến lược lập chỉ mục của mình.

1. Hợp nhất với Compound Index

Một compound index được lập kế hoạch tốt như { shop_id: 1, status: 1, category: 1 } có thể hỗ trợ ba loại truy vấn khác nhau. Nó bao phủ các tìm kiếm chỉ trên shop_id, shop_id + status, và cả ba trường kết hợp. Hãy sử dụng quy tắc "ESR" (Equality, Sort, Range) để làm cho các index của bạn linh hoạt hơn.

2. Chuyển sang Wildcard Index

Bạn đang lập chỉ mục cho hàng tá trường người dùng tùy chỉnh hoặc metadata động? Hãy dừng lại. Thay vào đó, hãy sử dụng Wildcard Index:

db.mycollection.createIndex({ "user_metadata.$**": 1 })

Điều này bao phủ mọi trường lồng nhau bên trong đối tượng user_metadata. Phần tốt nhất là? Nó chỉ tính là một index trong giới hạn 64 index của bạn, bất kể nó theo dõi bao nhiêu trường con.

3. Xác định các index không sử dụng với $indexStats

Đừng đoán xem index nào hữu ích. Hãy sử dụng aggregation $indexStats để xem mức độ sử dụng thực tế kể từ lần khởi động lại cuối cùng:

db.mycollection.aggregate([ { $indexStats: {} } ])

Kiểm tra trường accesses.ops. Nếu một index hiển thị 0 thao tác sau một tuần chạy traffic thực tế (production), bạn có thể an tâm xóa nó.

Xác minh

Sau khi dọn dẹp, hãy thử tạo lại index mới của bạn. Bây giờ nó sẽ thành công:

db.mycollection.createIndex({ "new_feature_key": 1 }, { name: "idx_feature_v1" })
// Trả về: { "ok": 1 }

Chạy db.mycollection.explain("executionStats").find(...) trên các truy vấn phổ biến của bạn để đảm bảo rằng việc xóa các index cũ không gây ra sụt giảm hiệu suất như quét toàn bộ collection (collection scan).

Danh sách kiểm tra phòng ngừa

  • Vô hiệu hóa Auto-Indexing: Trong Mongoose, hãy đặt autoIndex: false. Quản lý các index của bạn thông qua các script migration để chúng không gây bất ngờ cho bạn trên môi trường production.
  • Giám sát giới hạn: Thiết lập cảnh báo trong MongoDB Atlas hoặc Datadog để kích hoạt khi một collection đạt đến 50 index.
  • Xem xét các TTL: Các index Time To Live (TTL) rất hữu ích nhưng cũng tính vào giới hạn. Hãy sử dụng chúng một cách tiết kiệm.
  • Kiểm tra định kỳ hàng quý: Lên lịch một tác vụ định kỳ để chạy $indexStats và loại bỏ các index không sử dụng.

Related Error Notes