You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NodeJS/TypeScript中原子更新Mongo文档的lastUpdatedTimestamp

在Node.js/TypeScript中实现MongoDB文档的条件原子更新(基于lastUpdatedTimestamp)

要解决这个竞态条件问题,核心是利用MongoDB的原子更新操作,把「时间戳对比」和「更新时间戳」合并为一个服务器端执行的原子命令,避免客户端读取后到写入前的中间状态被其他请求篡改。

下面分别给出基于原生MongoDB驱动和Mongoose两种常用方式的实现:

1. 基于原生MongoDB驱动实现

首先确保已安装依赖包:

npm install mongodb @types/mongodb

实现代码:

import { MongoClient, ObjectId } from 'mongodb';

// 请求参数类型定义
interface UpdateRequest {
  docId: string;
  clientLastUpdatedTimestamp: number; // 客户端传来的毫秒级时间戳
}

async function updateDocumentIfTimestampMatches(req: UpdateRequest) {
  const client = new MongoClient('mongodb://localhost:27017');
  
  try {
    await client.connect();
    const db = client.db('your-database-name');
    const collection = db.collection('your-collection-name');

    // 原子更新:仅当数据库中lastUpdatedTimestamp与请求值匹配时才执行更新
    const result = await collection.updateOne(
      {
        _id: new ObjectId(req.docId),
        lastUpdatedTimestamp: req.clientLastUpdatedTimestamp // 匹配条件
      },
      {
        $set: {
          lastUpdatedTimestamp: Date.now() // 更新为当前毫秒级时间戳
        }
      }
    );

    // 判断更新结果
    if (result.modifiedCount === 1) {
      return { success: true, message: '文档更新成功' };
    } else {
      return { success: false, message: '时间戳不匹配,文档未更新(可能已被其他请求修改)' };
    }
  } finally {
    await client.close();
  }
}

2. 基于Mongoose实现

如果使用Mongoose(更适配TypeScript类型约束),先安装依赖:

npm install mongoose @types/mongoose

实现代码:

import mongoose from 'mongoose';

// 定义文档的TypeScript接口与Schema
interface YourDocument extends mongoose.Document {
  lastUpdatedTimestamp: number;
  // 其他业务字段...
}

const YourSchema = new mongoose.Schema<YourDocument>({
  lastUpdatedTimestamp: { type: Number, required: true }
  // 其他字段定义...
});

const YourModel = mongoose.model<YourDocument>('YourModel', YourSchema);

// 请求参数类型
interface UpdateRequest {
  docId: string;
  clientLastUpdatedTimestamp: number;
}

async function updateDocumentIfTimestampMatches(req: UpdateRequest) {
  await mongoose.connect('mongodb://localhost:27017/your-database-name');

  try {
    // 执行原子更新操作
    const updateResult = await YourModel.findOneAndUpdate(
      {
        _id: req.docId,
        lastUpdatedTimestamp: req.clientLastUpdatedTimestamp
      },
      { $set: { lastUpdatedTimestamp: Date.now() } },
      { rawResult: true } // 获取原生更新结果用于判断
    );

    if (updateResult?.rawResult?.nModified === 1) {
      return { success: true };
    } else {
      return { success: false, message: '时间戳不匹配或文档不存在,更新失败' };
    }
  } finally {
    await mongoose.disconnect();
  }
}

关键说明

  • 原子性保证:updateOne或findOneAndUpdate都是MongoDB的原子操作,服务器会原子性完成「条件匹配」与「字段更新」,不会出现中间状态。
  • 结果判断:通过modifiedCount(原生驱动)或nModified(Mongoose原生结果)判断是否有文档被更新。若值为0,说明请求的时间戳与数据库中当前值不匹配,大概率是文档已被其他请求修改。
  • 时间戳格式兼容:如果你的文档使用ISODate类型存储时间戳,只需将条件和更新值替换为new Date(req.clientLastUpdatedTimestamp)和new Date()即可。

内容的提问来源于stack exchange,提问作者HelmBurger

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 21:13:35