Express JS如何处理并发请求避免冲突 自定义自增ID生成场景疑问
问题解答
问题一:相同srcUser和dstUser的并发请求是否会出现ID冲突?
- 答案是会出现冲突。Node.js虽然是单线程执行JS代码,但只要遇到
await这类异步操作,就会暂时中断当前请求的处理,让出事件循环去处理其他请求。 - 你的场景里两个相同参数的请求几乎同时进来时,会先后执行到
await Issue.find(),此时两个请求都还没有把新的issue写入数据库,所以拿到的count值完全相同,最终生成的ID也会重复,写入时就会出现冲突。
问题二:单CPU核心下Express异步处理请求的逻辑理解是否正确?
- 你的理解完全正确。Node.js的事件循环模型决定了,遇到异步IO操作(比如数据库查询、网络请求)时,不会阻塞等待操作完成,而是会继续处理后续的新请求,等异步操作返回结果后,再回头继续执行之前请求剩下的逻辑。
问题三:多CPU核心下Node.js/Express是否会默认利用所有核心?
- 不会,单个Node.js进程默认只能使用一个CPU核心,因为JS的执行主线程是单线程的。
- 如果要利用多核心,你需要用Node.js自带的
cluster模块,或者用PM2这类工具启动多个Node.js进程实例,每个实例独立运行、各自处理请求。这种场景下上面的ID冲突问题会更严重,因为多进程之间没有共享内存,没法直接做并发状态同步。
附带ID冲突问题的解决方案
你可以根据业务需求选其中一种:
- 数据库层面给issue的
id字段加唯一约束,即使业务代码生成了重复ID,插入时会抛出错误,你捕获错误后重试生成ID即可,实现成本最低。 - 改用乐观锁/悲观锁控制并发:查询计数的时候给对应的查询行加锁,或者写入的时候判断当前计数是否和查询时一致,不一致就重试。
- 放弃自定义拼接计数ID的方案,直接用UUID、雪花ID这类天生唯一的ID生成方案,完全避免冲突问题。
- 如果必须保留
srcUser-dstUser-[number]的业务ID格式,可以单独维护一张计数表,专门记录每组srcUser+dstUser对应的最新序号,更新序号时用原子操作,比如执行UPDATE count_table SET count = count +1 WHERE src_user = ? AND dst_user = ? RETURNING count来拿到最新的序号,保证原子性。
内容的提问来源于stack exchange,提问作者Kid_Learning_C
相关产品推荐
相关产品推荐

