Sửa lỗi 'Index with name already exists with different options' trong MongoDB

intermediate🍃 MongoDB2026-04-07| MongoDB 3.6+, Linux, Docker, macOS, Node.js/Python/Go drivers

Error Message

Index with name: [index_name] already exists with different options
#mongodb#database-administration#ttl-index#backend-development

Vấn đềBạn cố gắng cập nhật một chỉ mục (index) trong MongoDB—chẳng hạn như kéo dài thời gian hết hạn phiên làm việc (session timeout)—và quá trình triển khai bị lỗi. Các dòng nhật ký (logs) hiển thị một lỗi cụ thể và dai dẳng: Index with name already exists with different options. Điều này xảy ra khi bạn cố gắng sửa đổi cấu hình của một chỉ mục hiện có, chẳng hạn như giá trị expireAfterSeconds hoặc ràng buộc unique, mà không thay đổi tên của chỉ mục đó.

Gần đây chúng tôi đã gặp phải vấn đề này khi mở rộng kho lưu trữ phiên làm việc cho môi trường production. Chúng tôi cần tăng TTL (Time-To-Live) từ 24 giờ (86.400 giây) lên 7 ngày (604.800 giây). Khi mã nguồn chuyển đổi (migration script) chạy, tiến trình dừng lại ngay lập tức vì chỉ mục createdAt_1_ttl đã tồn tại với các thiết lập cũ.

Tại sao lỗi này xảy raMongoDB coi createIndex() là một thao tác có tính idempotent (không thay đổi kết quả khi thực hiện nhiều lần). Nếu chỉ mục đã tồn tại chính xác như định nghĩa, cơ sở dữ liệu sẽ không làm gì và trả về thông báo thành công. Tuy nhiên, định nghĩa phải khớp hoàn toàn 1:1. Nếu bạn thay đổi dù chỉ một thuộc tính, MongoDB sẽ coi đó là một sự xung đột. Nó sẽ không tự động ghi đè lên cấu hình chỉ mục hiện có để ngăn ngừa mất dữ liệu ngoài ý muốn hoặc ảnh hưởng đến hiệu suất. Thay vào đó, nó sẽ ném ra một lỗi và chờ sự can thiệp thủ công.

Giải pháp: Hai cách tiếp cận### Cách 1: Lệnh 'collMod' (Khuyên dùng cho TTL)Nếu bạn chỉ thay đổi giá trị expireAfterSeconds, đừng xóa chỉ mục. Việc xây dựng lại chỉ mục trên một collection có 10 triệu dòng là một sự lãng phí tài nguyên CPU và I/O. Thay vào đó, hãy sử dụng lệnh collMod (collection modification). Lệnh này cập nhật siêu dữ liệu TTL ngay lập tức mà không cần xây dựng lại cấu trúc dữ liệu bên dưới.

Thực thi lệnh này trong MongoDB shell:

db.runCommand({
  collMod: "sessions",
  index: {
    keyPattern: { "createdAt": 1 },
    expireAfterSeconds: 604800
  }
})

Quan trọng: Khi sử dụng collMod, bạn phải xác định chỉ mục bằng keyPattern (các trường mà nó bao phủ), chứ không phải bằng tên của nó.

Cách 2: Xóa và tạo lại (Cho các tùy chọn Unique hoặc Sparse)Lệnh collMod có những hạn chế nhất định. Nếu bạn đang thêm unique: true, sparse: true, hoặc thay đổi biểu thức lọc một phần (partial filter expression), bạn phải thay thế hoàn toàn chỉ mục. Hãy làm theo các bước sau một cách cẩn thận để giảm thiểu thời gian gián đoạn (downtime).

Bước 1: Tìm tên chính xác của chỉ mục

db.your_collection.getIndexes()

Bước 2: Xóa chỉ mục cũ

db.your_collection.dropIndex("email_1")

Bước 3: Tạo chỉ mục mới

db.your_collection.createIndex(
  { "email": 1 },
  { name: "email_1", unique: true }
)

Lưu ý: Trên MongoDB 4.2 và mới hơn, tất cả các quá trình xây dựng chỉ mục đều không gây nghẽn (non-blocking) theo mặc định. Nếu bạn đang dùng phiên bản cũ hơn, hãy thêm { background: true } vào các tùy chọn để ứng dụng của bạn vẫn có thể phản hồi trong khi xây dựng chỉ mục.

Xác minh thay đổiLuôn kiểm tra lại công việc của bạn. Chạy getIndexes() và tìm trường expireAfterSeconds trong kết quả trả về để xác nhận giá trị mới đã có hiệu lực:

db.sessions.getIndexes()

Kết quả sẽ phản ánh cấu hình mới của bạn:

[
  {
    "v": 2,
    "key": { "createdAt": 1 },
    "name": "createdAt_1_ttl",
    "expireAfterSeconds": 604800
  }
]

Các thực hành tốt nhất để quản lý chỉ mụcCập nhật chỉ mục thủ công rất dễ dẫn đến sai sót do con người. Hãy sử dụng các chiến lược sau để giữ cho cơ sở dữ liệu của bạn luôn ổn định:

  • Tự động hóa với Migrations: Sử dụng các công cụ như db-migrate hoặc các script tùy chỉnh để kiểm tra các chỉ mục hiện có trước khi cố gắng xây dựng. Các script này nên xử lý logic collMod thay cho bạn.- Đặt tên theo phiên bản: Nếu bạn đang thực hiện thay đổi về cấu trúc, hãy cân nhắc việc đặt tên theo phiên bản (ví dụ: email_1_v2). Điều này cho phép bạn xây dựng chỉ mục mới ở chế độ chạy ngầm (background) trong khi chỉ mục cũ vẫn đang phục vụ các truy vấn.- Kiểm tra trước khi triển khai: Trước khi chạy migration, hãy so sánh định nghĩa chỉ mục ở môi trường local với môi trường production. Điều này ngăn chặn lỗi "different options" xuất hiện trong quá trình phát hành quan trọng.

Related Error Notes