如何仅更新JSON文件部分内容而非整体覆盖 避免多用户修改数据冲突
解决方案
首先需要明确:JSON 是纯文本结构化格式,没有内置的局部写入支持,不存在真正意义上的「仅修改对应用户所属JSON片段」的操作——哪怕你只想改某一个用户的数值,只要数值长度发生变化,后续所有文本都需要移位重写,本质还是要覆盖整个文件。你遇到的并发覆盖问题核心是读写操作没有互斥,不需要切换数据库,用以下两种低成本方案即可解决:
方案1:单文件加串行读写队列(改造成本最低,适配现有存储结构)
把所有对JSON文件的读写操作都放到同一个串行执行队列中,保证同一时间只有一个操作在执行,从根源避免并发读写冲突,不需要调整现有存储结构,改造成本极低。
示例代码(Node.js 环境):
const fs = require('fs/promises'); const path = require('path'); const userDataPath = path.join(__dirname, 'users.json'); // 全局操作锁 + 任务队列 let isProcessing = false; const taskQueue = []; // 统一的用户数据修改入口 async function updateUserData(userId, updateFn) { return new Promise((resolve, reject) => { // 操作压入队列 taskQueue.push({ userId, updateFn, resolve, reject }); // 无正在执行的操作则触发队列处理 if (!isProcessing) processQueue(); }); } async function processQueue() { if (taskQueue.length === 0) { isProcessing = false; return; } isProcessing = true; const currentTask = taskQueue.shift(); try { // 读取当前最新的完整数据 const rawData = await fs.readFile(userDataPath, 'utf-8'); const allUserData = JSON.parse(rawData); // 仅修改对应用户的数据 const targetUser = allUserData[currentTask.userId] || { balance: 0 }; allUserData[currentTask.userId] = currentTask.updateFn(targetUser); // 写回完整文件 await fs.writeFile(userDataPath, JSON.stringify(allUserData, null, 2)); currentTask.resolve(allUserData[currentTask.userId]); } catch (err) { currentTask.reject(err); } finally { // 处理下一个任务 processQueue(); } }
使用示例:
// 给ID为123456的用户增加100金币 updateUserData('123456', (user) => { user.balance += 100; return user; });
方案2:拆分单用户独立存储(性能更高,完全避免跨用户影响)
如果你的用户规模不大(单服用户量小于10万),可以把每个用户的数据单独存为一个JSON文件,存储路径示例为./users/{用户ID}.json。这种方案下不同用户的读写操作完全独立,不会互相影响,甚至不需要加锁就能避免覆盖问题,并发性能远高于单文件方案。
示例代码:
const fs = require('fs/promises'); const path = require('path'); const userDir = path.join(__dirname, 'users'); // 提前创建用户存储目录 fs.access(userDir).catch(() => fs.mkdir(userDir, { recursive: true })); async function updateUserData(userId, updateFn) { const userFilePath = path.join(userDir, `${userId}.json`); let userData = { balance: 0 }; // 读取对应用户的独立数据文件,不存在则初始化 try { const rawData = await fs.readFile(userFilePath, 'utf-8'); userData = JSON.parse(rawData); } catch (err) { if (err.code !== 'ENOENT') throw err; } // 修改用户数据 userData = updateFn(userData); // 仅写回当前用户的独立文件,完全不影响其他用户数据 await fs.writeFile(userFilePath, JSON.stringify(userData, null, 2)); return userData; }
可选优化:加内存缓存减少IO开销
如果你的机器人重启频率较低,可以新增一层内存缓存存储活跃用户的数据,定时批量将缓存数据落盘到文件,能进一步降低IO开销提升性能,注意添加异常落盘逻辑避免程序崩溃丢失数据。
内容的提问来源于stack exchange,提问作者Tesohh
相关产品推荐
相关产品推荐

