订单冲突靠redis分布式锁在入口拦截,核心是原子加锁(set key value nx px)、安全释放(lua校验token)和合理超时(预估耗时+缓冲或看门狗续期),并辅以数据库唯一索引兜底。

订单冲突不是靠“运气”避开的,而是靠锁在入口处拦住重复请求。PHP框架里集成Redis分布式锁,核心就三件事:加锁要原子、释放要安全、超时要合理。它不解决所有并发问题,但能大幅降低数据库压力和重复下单概率。
加锁必须用一条原子命令
别再写SETNX + EXPIRE两步操作——中间宕机就死锁。直接用SET key value NX PX 10,其中:
- NX 确保只在键不存在时才设置,天然互斥
- PX 10 表示10毫秒级精度过期(单位毫秒),避免无限占用
-
value必须是唯一标识,比如
uniqid().rand(1000,9999),后续释放时靠它验明正身
释放锁绝不能用DEL硬删
直接$redis->del($key)会误删别人刚拿到的锁。必须用Lua脚本保证“读-判-删”三步不可拆分:
- 脚本内容:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end - PHP调用:
$redis->eval($luaScript, [$lockKey], [$token]) - 只有token匹配才真正删除,否则返回0,业务层据此判断是否释放成功
锁时间不是越长越保险
设30秒锁?业务5秒就跑完,剩下25秒纯浪费;设3秒?网络抖动或GC暂停可能让锁提前失效,导致并发重入。建议:
- 预估业务最大耗时,再加2~3秒缓冲(如订单创建通常≤8秒,锁设10秒较稳)
- 对超长任务(如异步通知回调),启用看门狗机制:另起一个轻量协程,每3秒续期一次TTL
- 绝不依赖锁时间兜底——数据库仍需唯一索引(如
order_no字段唯一)作为最后一道防线
框架集成要注意实际落地细节
Hyperf、Laravel、ThinkPHP等主流框架都有封装好的锁组件,但别直接照搬默认配置:
- 确认底层是否真用了
NX PX原子命令,而非模拟实现 - 检查释放逻辑是否含Lua校验,有些包只做简单DEL
- 留意连接池配置:高并发下Redis连接耗尽比锁失败更致命,建议连接数≥QPS峰值×0.5
- 测试时故意kill进程,验证锁能否自动过期,而不是靠人工清理
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











