Amazon DocumentDB 文档删除 #

一、删除方法概述 #

1.1 删除方法 #

text
删除方法:
├── deleteOne - 删除单个文档
├── deleteMany - 删除多个文档
├── findOneAndDelete - 查找并删除
└── remove - 已废弃,不推荐使用

1.2 删除注意事项 #

text
删除前考虑:
├── 确认删除条件
├── 检查是否有备份数据
├── 考虑使用软删除
├── 检查关联数据
└── 确认权限

二、deleteOne - 删除单个文档 #

2.1 基本用法 #

javascript
// 删除单个文档
db.users.deleteOne({ _id: ObjectId("...") })

// 返回结果
{
  "acknowledged": true,
  "deletedCount": 1
}

2.2 条件删除 #

javascript
// 根据条件删除
db.users.deleteOne({ email: "test@example.com" })

// 删除指定条件的第一个文档
db.logs.deleteOne(
  { level: "debug" },
  { sort: { timestamp: 1 } }  // 删除最早的
)

2.3 删除选项 #

javascript
// 带写关注删除
db.users.deleteOne(
  { _id: ObjectId("...") },
  { writeConcern: { w: "majority" } }
)

三、deleteMany - 删除多个文档 #

3.1 基本用法 #

javascript
// 删除多个文档
db.users.deleteMany({ status: "inactive" })

// 返回结果
{
  "acknowledged": true,
  "deletedCount": 150
}

3.2 条件删除 #

javascript
// 删除过期数据
db.logs.deleteMany({
  timestamp: { $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
})

// 删除无效数据
db.users.deleteMany({
  $or: [
    { email: { $exists: false } },
    { email: null },
    { email: "" }
  ]
})

// 删除测试数据
db.products.deleteMany({
  isTest: true
})

3.3 删除所有文档 #

javascript
// 删除集合中所有文档
db.users.deleteMany({})

// 注意:这会删除所有文档,但保留集合和索引

四、findOneAndDelete #

4.1 基本用法 #

javascript
// 查找并删除,返回被删除的文档
const deletedDoc = db.users.findOneAndDelete(
  { email: "test@example.com" }
)

// 返回被删除的文档
{
  "_id": ObjectId("..."),
  "name": "测试用户",
  "email": "test@example.com"
}

4.2 带排序删除 #

javascript
// 删除最早的日志
const oldestLog = db.logs.findOneAndDelete(
  { level: "debug" },
  { sort: { timestamp: 1 } }
)

// 删除优先级最低的任务
const task = db.tasks.findOneAndDelete(
  { status: "pending" },
  { sort: { priority: 1, createdAt: 1 } }
)

4.3 带投影删除 #

javascript
// 只返回指定字段
const result = db.users.findOneAndDelete(
  { _id: ObjectId("...") },
  { projection: { name: 1, email: 1 } }
)

五、软删除模式 #

5.1 软删除实现 #

javascript
// 添加删除标记而不是真正删除
db.users.updateOne(
  { _id: ObjectId("...") },
  { 
    $set: { 
      deleted: true,
      deletedAt: new Date(),
      deletedBy: "admin"
    }
  }
)

// 查询时排除已删除文档
db.users.find({ deleted: { $ne: true } })

// 查询已删除文档
db.users.find({ deleted: true })

5.2 软删除索引 #

javascript
// 创建部分索引,排除已删除文档
db.users.createIndex(
  { email: 1 },
  { 
    unique: true,
    partialFilterExpression: { deleted: { $ne: true } }
  }
)

5.3 恢复软删除 #

javascript
// 恢复已删除文档
db.users.updateOne(
  { _id: ObjectId("..."), deleted: true },
  { 
    $set: { deleted: false },
    $unset: { deletedAt: "", deletedBy: "" }
  }
)

5.4 定期清理 #

javascript
// 定期永久删除软删除数据
db.users.deleteMany({
  deleted: true,
  deletedAt: { $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
})

六、批量删除策略 #

6.1 分批删除 #

javascript
// 大数据量分批删除
function batchDelete(collection, filter, batchSize = 1000) {
  let totalDeleted = 0;
  
  while (true) {
    const result = collection.deleteMany(
      filter,
      { limit: batchSize }
    );
    
    totalDeleted += result.deletedCount;
    print(`Deleted ${result.deletedCount} documents, total: ${totalDeleted}`);
    
    if (result.deletedCount < batchSize) {
      break;
    }
    
    // 暂停一下,避免压力过大
    sleep(100);
  }
  
  return totalDeleted;
}

// 使用示例
batchDelete(db.logs, { timestamp: { $lt: new Date("2023-01-01") } });

6.2 使用聚合删除 #

javascript
// 查找并删除
const idsToDelete = db.logs.find({
  timestamp: { $lt: new Date("2023-01-01") }
}, { _id: 1 }).map(doc => doc._id);

db.logs.deleteMany({
  _id: { $in: idsToDelete }
});

七、级联删除 #

7.1 手动级联删除 #

javascript
// 删除用户及其关联数据
async function deleteUserWithRelations(userId) {
  const session = client.startSession();
  
  try {
    session.startTransaction();
    
    // 删除用户订单
    await db.orders.deleteMany(
      { userId: userId },
      { session }
    );
    
    // 删除用户评论
    await db.comments.deleteMany(
      { userId: userId },
      { session }
    );
    
    // 删除用户
    await db.users.deleteOne(
      { _id: userId },
      { session }
    );
    
    await session.commitTransaction();
    print("用户及相关数据删除成功");
  } catch (error) {
    await session.abortTransaction();
    print("删除失败: " + error.message);
  } finally {
    await session.endSession();
  }
}

7.2 使用变更流实现 #

javascript
// 监听删除事件,自动清理关联数据
const changeStream = db.users.watch([
  { $match: { operationType: "delete" } }
]);

changeStream.on("change", async (change) => {
  const userId = change.documentKey._id;
  
  // 删除关联数据
  await db.orders.deleteMany({ userId: userId });
  await db.comments.deleteMany({ userId: userId });
});

八、删除性能优化 #

8.1 索引使用 #

javascript
// 确保删除条件使用索引
db.logs.createIndex({ timestamp: 1 })

// 使用索引字段删除
db.logs.deleteMany({
  timestamp: { $lt: new Date("2023-01-01") }
})

8.2 删除vs truncate #

text
性能对比:
├── deleteMany({})
│   ├── 保留索引
│   ├── 记录操作日志
│   ├── 可以回滚
│   └── 较慢
│
└── drop() + createCollection()
    ├── 删除所有索引
    ├── 不记录操作日志
    ├── 无法回滚
    └── 更快

8.3 优化建议 #

text
删除优化:
├── 使用索引字段作为条件
├── 大批量删除分批进行
├── 低峰期执行大批量删除
├── 考虑使用TTL索引自动删除
└── 监控删除性能

九、TTL索引自动删除 #

9.1 创建TTL索引 #

javascript
// 创建TTL索引,自动删除过期文档
db.sessions.createIndex(
  { createdAt: 1 },
  { expireAfterSeconds: 3600 }  // 1小时后删除
)

// 插入文档
db.sessions.insertOne({
  sessionId: "session123",
  userId: "user001",
  createdAt: new Date()
})

9.2 TTL索引特点 #

text
TTL索引说明:
├── 后台线程定期扫描
├── 删除时间不精确(约60秒扫描一次)
├── 只能用于单字段索引
├── 字段必须是日期类型
└── 不影响查询性能

十、删除安全最佳实践 #

10.1 删除前验证 #

javascript
// 安全删除函数
function safeDelete(collection, filter, options = {}) {
  // 检查删除条件
  if (!filter || Object.keys(filter).length === 0) {
    throw new Error("删除条件不能为空");
  }
  
  // 预览将要删除的文档
  const count = collection.countDocuments(filter);
  print(`将要删除 ${count} 个文档`);
  
  if (options.preview) {
    return collection.find(filter).limit(10).toArray();
  }
  
  // 确认删除
  if (options.confirm !== true) {
    throw new Error("需要确认删除操作");
  }
  
  return collection.deleteMany(filter);
}

// 使用示例
safeDelete(db.users, { status: "inactive" }, { preview: true });
safeDelete(db.users, { status: "inactive" }, { confirm: true });

10.2 删除审计 #

javascript
// 记录删除操作
async function deleteWithAudit(collection, filter, operator) {
  // 查找要删除的文档
  const docsToDelete = await collection.find(filter).toArray();
  
  // 记录到审计日志
  await db.auditLogs.insertMany(docsToDelete.map(doc => ({
    operation: "delete",
    collection: collection.collectionName,
    documentId: doc._id,
    document: doc,
    operator: operator,
    timestamp: new Date()
  })));
  
  // 执行删除
  return collection.deleteMany(filter);
}

10.3 权限控制 #

javascript
// 创建只读用户(不能删除)
db.createUser({
  user: "readonly",
  pwd: "password",
  roles: [
    { role: "read", db: "mydb" }
  ]
})

// 创建有删除权限的用户
db.createUser({
  user: "admin",
  pwd: "password",
  roles: [
    { role: "readWrite", db: "mydb" }
  ]
})

十一、常见问题 #

11.1 删除后空间未释放 #

text
说明:
├── DocumentDB不会立即释放磁盘空间
├── 空间标记为可重用
├── 后续写入会优先使用
└── 可通过compact命令整理

11.2 删除操作超时 #

javascript
// 问题:大批量删除超时
// 解决:分批删除
function batchDeleteSafe(collection, filter, batchSize = 1000) {
  let deleted = 0;
  
  while (true) {
    // 获取一批ID
    const ids = collection.find(filter, { _id: 1 })
      .limit(batchSize)
      .map(doc => doc._id);
    
    if (ids.length === 0) break;
    
    // 删除这批文档
    const result = collection.deleteMany({ _id: { $in: ids } });
    deleted += result.deletedCount;
    
    if (ids.length < batchSize) break;
  }
  
  return deleted;
}

11.3 误删除恢复 #

text
恢复方法:
├── 从备份恢复
├── 使用时间点恢复(PITR)
├── 从审计日志恢复
└── 使用变更流日志

十二、总结 #

12.1 删除方法对比 #

方法 用途 返回值
deleteOne 删除单个文档 删除计数
deleteMany 删除多个文档 删除计数
findOneAndDelete 查找并删除 被删除文档

12.2 最佳实践总结 #

text
删除最佳实践:
├── 使用软删除保护重要数据
├── 删除前预览和确认
├── 记录删除审计日志
├── 大批量删除分批进行
├── 使用TTL索引自动清理
└── 确保有备份恢复方案

下一步,让我们学习基础查询!

最后更新:2026-03-27