Fix MongoServerError: E11000 duplicate key error collection trong MongoDB

beginner🍃 MongoDB2026-03-21| MongoDB 4.x–7.x, Node.js (Mongoose), Python (PyMongo), mọi hệ điều hành

Error Message

MongoServerError: E11000 duplicate key error collection
#mongodb#trùng lặp#index#unique

Lỗi Gặp Phải

Bạn chèn hoặc cập nhật một document và MongoDB trả về lỗi:

MongoServerError: E11000 duplicate key error collection: mydb.users index: email_1 dup key: { email: "john@example.com" }

Thông báo lỗi khá dễ đọc. Nó cho bạn biết collection (mydb.users), index đã kích hoạt lỗi (email_1), và giá trị bị trùng lặp chính xác ("john@example.com"). MongoDB có một unique index trên field đó — và giá trị này đã tồn tại rồi.

Nguyên Nhân

Unique index đảm bảo không có hai document nào cùng chia sẻ một giá trị cho field được đánh index. E11000 xuất hiện trong bốn tình huống phổ biến:

  • Chèn một document có giá trị field đã tồn tại trong collection.
  • Một thao tác upsert nhầm lẫn tạo document mới thay vì cập nhật document hiện có.
  • Thêm unique index vào collection đã có giá trị trùng lặp — MongoDB từ chối việc tạo index hoàn toàn.
  • Race condition: hai request đồng thời chèn cùng một giá trị trong vài mili giây.

Cách Sửa 1: Tìm và Xóa Các Bản Ghi Trùng Lặp

Bắt đầu bằng cách hiểu những gì đang có trong collection. Đừng đoán mò — hãy truy vấn trực tiếp:

// Find all documents with the duplicate value
db.users.find({ email: "john@example.com" })

// Count duplicates across the whole collection
db.users.aggregate([
  { $group: { _id: "$email", count: { $sum: 1 } } },
  { $match: { count: { $gt: 1 } } }
])

Khi đã biết document nào bị trùng, giữ lại cái bạn cần và xóa phần còn lại:

// Delete the older document by its _id
db.users.deleteOne({ _id: ObjectId("64a1b2c3d4e5f6a7b8c9d0e1") })

Cách Sửa 2: Dùng upsert Đúng Cách

Khi upsert: true được đặt, MongoDB tìm document khớp với filter của bạn. Nếu không tìm thấy, nó sẽ chèn một document mới. Đây chính là chỗ xảy ra lỗi — nếu filter không bao gồm field unique, MongoDB sẽ tạo ra bản trùng lặp thay vì cập nhật document hiện có:

// Wrong — filter doesn't target the unique field
db.users.updateOne(
  { name: "John" },
  { $set: { email: "john@example.com" } },
  { upsert: true }
)

// Correct — filter on the unique field
db.users.updateOne(
  { email: "john@example.com" },
  { $set: { name: "John" } },
  { upsert: true }
)

Cách Sửa 3: Xóa hoặc Tạo Lại Unique Index

Đôi khi chính ràng buộc unique mới là vấn đề — schema của bạn đã thay đổi, hoặc bạn chưa từng cần nó. Hãy xóa nó đi:

// List indexes to find the right name
db.users.getIndexes()

// Drop the unique index
db.users.dropIndex("email_1")

// Recreate without unique if needed
db.users.createIndex({ email: 1 })

Muốn giữ tính duy nhất nhưng dữ liệu hiện tại có bản trùng? Hãy dọn dẹp trước, rồi tạo lại index:

// After removing duplicates, recreate the unique index
db.users.createIndex({ email: 1 }, { unique: true })

Cách Sửa 4: Bắt Mã Lỗi 11000 Trong Code Ứng Dụng

Đăng ký người dùng, gửi form, API endpoint — bất cứ nơi nào người dùng cung cấp dữ liệu, bản trùng lặp là chuyện bình thường. Hãy bắt lỗi và trả về thông báo rõ ràng thay vì để exception lan ra ngoài:

// Node.js / Mongoose
try {
  await User.create({ email: "john@example.com" });
} catch (err) {
  if (err.code === 11000) {
    const field = Object.keys(err.keyValue)[0];
    return res.status(409).json({ error: `${field} already exists` });
  }
  throw err;
}
# Python / PyMongo
from pymongo.errors import DuplicateKeyError

try:
    collection.insert_one({ "email": "john@example.com" })
except DuplicateKeyError as e:
    print(f"Duplicate entry: {e.details['keyValue']}")

Cách Sửa 5: Race Condition — Chèn Đồng Thời

Hai request đến trong cùng một mili giây. Cả hai đều kiểm tra document hiện có, không tìm thấy gì, và cùng thử chèn. Một cái thành công. Cái còn lại nhận E11000.

Unique index đang hoạt động đúng — nó đang bảo vệ dữ liệu của bạn. Hãy xử lý lỗi ở tầng ứng dụng (bắt mã lỗi 11000 như đã hướng dẫn ở trên), hoặc chuyển sang dùng findOneAndUpdate dạng atomic với upsert:

db.users.findOneAndUpdate(
  { email: "john@example.com" },
  { $setOnInsert: { email: "john@example.com", createdAt: new Date() } },
  { upsert: true, returnDocument: "after" }
)

MongoDB thực thi thao tác này như một atomic operation duy nhất. Nó hoặc tìm document hiện có hoặc chèn một document mới — không bao giờ cả hai.

Phòng Ngừa

  • Kiểm tra trước khi import hàng loạt — chạy aggregation kiểm tra trùng lặp trước khi tải dataset lớn, đừng để hậu quả rồi mới xử lý.
  • Dùng ordered: false khi insert hàng loạt — MongoDB bỏ qua các bản trùng và tiếp tục xử lý phần còn lại thay vì dừng lại ở lỗi đầu tiên:

db.users.insertMany(docs, { ordered: false })

  
  - **Luôn bắt mã lỗi 11000** — trong mọi route hoặc service ghi vào collection có unique index.
  - **Dọn dữ liệu trước khi thêm unique index** — chạy kiểm tra trùng lặp, xóa các bản thừa, rồi mới tạo index.

## Kiểm Tra Sau Khi Sửa
Chạy lại kiểm tra trùng lặp. Kết quả phải trống:

// Should return nothing db.users.aggregate([ { $group: { _id: "$email", count: { $sum: 1 } } }, { $match: { count: { $gt: 1 } } } ])

// Retry the insert — should succeed now db.users.insertOne({ email: "john@example.com", name: "John" }) // { acknowledged: true, insertedId: ObjectId("...") }

Related Error Notes