跳转到主内容
极星编程网:以代码为星,赴技术山海!

Yii框架的Redis锁怎么实现_防止并发重复提交【经验】

Redis锁在Yii中应使用yii\redis\Connection的executeCommand方法配合SET NX EX实现原子加锁,解锁需用Lua脚本校验token,key须带业务上下文且长度≤128字符,token需唯一,超时时间按业务耗时2~3倍设置。 Redis锁在Yii里用
yii\redis\Connection
直接操作最稳 Yii自带的
yii\redis\Connection
提供了原生命令支持,比封装层更可控。很多项目图省事用
yii\redis\Cache
做锁,结果发现
set()
不带
NX
和
EX
参数,根本没法原子性加锁。 常见错误现象:
set('lock:order:123', '1')
后没校验是否真拿到了锁,多个请求同时写入,锁失效;或者用
exists()
+
set()
两步操作,中间被其他请求插队。 必须用
executeCommand('SET', ['lock:key', 'token', 'NX', 'EX', 30])
,
NX
保证只在 key 不存在时设置,
EX
防止死锁 返回值是
'OK'
才算真正拿到锁,其他情况(如
false
或
null
)要立刻拒绝后续逻辑 不要依赖
yii\redis\Cache::set()
,它不支持原子性条件写入 释放锁必须用 Lua 脚本校验 token 直接
del('lock:key')
是危险的——A 拿到锁,超时前没执行完,B 又抢到了新锁,这时 A 一删就把 B 的锁干掉了。Yii 没内置解锁脚本,得自己写。 使用场景:订单提交、库存扣减、支付回调幂等处理,任何需要“谁加的锁谁才能解”的地方。 解锁脚本必须先
GET
再比对 token,再
DEL
,三步必须原子执行 推荐这段 Lua:
if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end
在 Yii 中调用:
$redis->executeCommand('EVAL', [$luaScript, 1, 'lock:key', $token])
,
$token
必须是加锁时生成的唯一值(比如
uniqid('', true)
) Yii控制器里加锁别卡在 beforeAction,得包住具体业务逻辑 有人把锁逻辑塞进
beforeAction()
,结果整个 action 生命周期都占着锁,包括渲染视图、日志记录这些无关操作,锁持有时间远超必要。 WOC-YII开源站群管理系统1.3 WOC-YII是rschome.com基于yii framework 1.1.8框架所开发的一款开源简易站群管理系统。它的功能与WOC完全一样。目前版本为V1.3,新版本正在开发中,同时欢迎大家参与到开发中来! WOC-YII 1.3在1.2的基础上优化了登录系统(密码加密),优化了权限控制系统,新增seo管理功能,新增自动安装向导! 程序框架:yiiframework1.1.8 配置文件:p 下载 性能影响:锁时间越长,并发吞吐越低;兼容性上,Redis 连接池也可能因阻塞变紧张。 锁只应包裹真正需要互斥的代码块,比如“查库存 → 扣减 → 写订单”这一段 记得 try/finally,确保无论成功失败都尝试解锁(但解锁失败不能抛异常中断主流程) 别在事务里嵌套 Redis 锁,MySQL 事务和 Redis 锁无关联,两者失败策略不同,容易误判 锁 key 设计要带业务上下文,避免全局冲突 用固定 key 如
lock:submit
看似简单,实际会让所有提交请求串行,完全丧失并发能力。真正的重复提交,往往针对同一笔订单、同一个用户、同一次表单 ID。 容易踩的坑:key 里拼接变量但没过滤特殊字符,导致 key 含空格或冒号,Redis 拒绝写入;或者 key 过长,影响 Redis 内存和集群分片。 推荐格式:
lock:order_submit:{orderId}
、
lock:form:{formId}
,用大括号明确占位符 orderId 等变量需做基础校验(如正则
/^\d+$/
),防止注入非法字符 key 总长度建议控制在 128 字符内,过长会影响 Redis Cluster 的 slot 计算 token 生成和锁超时时间这两个点,最容易被忽略:token 不唯一会导致误删,超时设太短会业务没跑完锁就没了,设太长又拖慢恢复速度。得按接口平均耗时 × 2~3 倍来定,再加一点缓冲。

相关文章