如何在Parse/Back4app中防止写入丢失?并发访问方案咨询
Parse/Back4app 乐观锁(CAS)实现方案
内置支持情况
Parse/Back4app 没有直接封装CAS(Compare-And-Swap)或MongoDB findAndModify 风格的乐观锁API,但可以利用其条件查询+原子更新的能力,手动实现类似JPA的乐观锁机制,核心就是基于version字段的版本校验。
手动实现乐观锁的步骤
1. 给对象类添加版本字段
在你的Parse对象类(比如YourClass)中添加一个version字段,类型设为Number,默认值设为0。
2. 查询对象时获取当前版本
从服务器获取对象时,同步拿到当前的version值,后续更新要基于这个版本做校验:
// JavaScript示例 const targetObj = await new Parse.Query('YourClass').get('目标对象ID'); const currentVersion = targetObj.get('version');
3. 带版本条件执行更新
更新操作时,必须添加version等于当前获取值的条件,同时将版本号自增1,确保只有版本匹配时才会执行更新:
方式一:查询后更新(适合需要先读取对象属性的场景)
const query = new Parse.Query('YourClass'); query.equalTo('objectId', '目标对象ID'); query.equalTo('version', currentVersion); const objToUpdate = await query.first(); if (objToUpdate) { // 设置需要更新的业务字段 objToUpdate.set('yourField', '新值'); // 版本号自增 objToUpdate.set('version', currentVersion + 1); await objToUpdate.save(); } else { // 版本不匹配,说明已有其他用户修改 throw new Error('对象已被其他用户修改,请重新加载后重试'); }
方式二:批量条件更新(更高效,无需先获取对象)
const query = new Parse.Query('YourClass'); query.equalTo('objectId', '目标对象ID'); query.equalTo('version', currentVersion); // 构造更新内容:业务字段+版本自增 const updateData = { yourField: '新值', version: currentVersion + 1 }; // 执行批量更新,返回成功更新的条数 const updatedCount = await Parse.Query.update(query, updateData); if (updatedCount === 0) { throw new Error('并发修改冲突,请重新操作'); }
4. 冲突处理逻辑
当更新返回的updatedCount为0,或者查询不到匹配版本的对象时,说明发生了并发修改。此时可以:
- 提示用户重新加载最新数据后再尝试修改
- 实现有限次数的自动重试(比如重试2-3次后再提示用户)
进阶优化方案
用云函数封装更新逻辑
把版本校验和更新的逻辑放到Parse云函数中,减少客户端的多步操作,同时保证逻辑在服务器端原子执行:
Parse.Cloud.define('updateWithOptimisticLock', async (request) => { const { objectId, currentVersion, updates } = request.params; const query = new Parse.Query('YourClass'); query.equalTo('objectId', objectId); query.equalTo('version', currentVersion); const updateData = { ...updates, version: currentVersion + 1 }; const updatedCount = await Parse.Query.update(query, updateData); if (updatedCount === 0) { throw new Parse.Error(Parse.Error.CONCURRENCY_ERROR, '对象已被其他用户修改'); } return { success: true, newVersion: currentVersion + 1 }; });
自托管Parse直接调用MongoDB findAndModify
如果你是自托管Parse服务器(而非Back4app托管),可以直接在云函数中调用MongoDB的findOneAndUpdate(对应findAndModify)命令,实现更原生的原子CAS操作:
Parse.Cloud.define('atomicUpdateWithLock', async (request) => { const { objectId, currentVersion, updates } = request.params; const dbClient = await Parse.mongoClient.MongoClient.connect(Parse.serverConfig.databaseURI); const collection = dbClient.collection('YourClass'); const result = await collection.findOneAndUpdate( { _id: Parse.Object.extend('YourClass').createWithoutData(objectId).id, version: currentVersion }, { $set: updates, $inc: { version: 1 } }, { returnDocument: 'after' } ); if (!result.value) { throw new Parse.Error(Parse.Error.CONCURRENCY_ERROR, '并发修改冲突'); } await dbClient.close(); return result.value; });
注意:Back4app作为托管服务,不允许直接操作底层MongoDB,因此该方案仅适用于自托管Parse环境。
内容的提问来源于stack exchange,提问作者gabrielgiussi
相关产品推荐
相关产品推荐

