PostgreSQL如何实现类似Firestore的原子增减 解决并发更新数值错误问题
基于PostgreSQL实现原子增减的方案
核心原理
PostgreSQL的单条UPDATE语句本身具备原子性,执行时会自动对目标行加排他锁,并发请求会按顺序依次执行更新,每一次的字段计算都会基于上一次更新后的最新值,天然避免竞态条件,默认的READ COMMITTED事务隔离级别即可满足需求,无需额外配置。
原生SQL实现
假设你的业务表名为posts,需要增减的计数字段为like_count,帖子主键为id,实现+1的SQL语句如下:
-- +1操作 UPDATE posts SET like_count = like_count + 1 WHERE id = 目标帖子ID; -- -1操作只需把+改成-即可 UPDATE posts SET like_count = like_count - 1 WHERE id = 目标帖子ID;
该写法下并发5个+1请求执行后,字段值会从10变为15,不会出现更新覆盖的问题。如果需要直接拿到更新后的最新值,可以在SQL末尾加RETURNING like_count,无需二次查询。
结合NodeJS + Express的代码实现
以最常用的pg(node-postgres)库为例,接口示例如下:
const { Pool } = require('pg'); const pool = new Pool({ // 替换为你的PostgreSQL连接配置 host: 'localhost', user: '数据库用户名', password: '数据库密码', database: '业务库名' }); // 帖子点赞接口示例 app.post('/posts/:id/like', async (req, res) => { const postId = req.params.id; try { const result = await pool.query( `UPDATE posts SET like_count = like_count + 1 WHERE id = $1 RETURNING like_count;`, [postId] ); if (result.rowCount === 0) { return res.status(404).json({ message: '帖子不存在' }); } // 直接返回最新计数值给前端React渲染 res.json({ likeCount: result.rows[0].like_count }); } catch (err) { console.error('更新失败:', err); res.status(500).json({ message: '操作失败' }); } });
ORM场景示例(可选)
如果你的项目使用Sequelize等ORM框架,无需手写SQL也可以调用封装好的原子增减方法,本质也是生成上述原子UPDATE语句:
// Sequelize 示例 const [updatedRows, [updatedPost]] = await Post.increment('like_count', { by: 1, // 增减数值,减1传-1即可 where: { id: postId }, returning: true // 开启后返回更新后的行数据 }); console.log(updatedPost.like_count); // 最新的计数值
TypeORM等其他ORM都有对应的原生增减方法,直接调用即可。
常见避坑
- 禁止使用「先SELECT查值 -> JS层做加减计算 -> 再UPDATE写回」的实现逻辑,该方案存在竞态条件,并发场景下必然出现更新覆盖的问题
- 若需要动态指定增减数值,直接将动态值作为SQL参数传入即可,语法为
UPDATE posts SET like_count = like_count + $2 WHERE id = $1,原子性不受影响 - 仅当存在多字段关联更新、带复杂条件的更新等需求时,才需要配合事务+行锁实现,纯±1场景无需额外加锁,默认配置即可保证并发安全
内容的提问来源于stack exchange,提问作者SURYA B
相关产品推荐
相关产品推荐

