添加唯一任务防止重复
foreach ($documents as $k => $v){
$domains[] = $v["domain"];
$lockKey = "domain_lock:".$v["domain"];
if (RedisUtil::setnx($lockKey, 1)) {
// 设置锁过期时间,防止死锁
RedisUtil::expire($lockKey, 3600); // 1小时过期
// 将域名添加到队列
RedisUtil::lpush("domain_queue", $v["domain"]);
}
}
更安全的写法
foreach ($documents as $k => $v){
$domains[] = $v["domain"];
$lockKey = "domain_lock:".$v["domain"];
if (RedisUtil::set($lockKey, 1, ['NX', 'EX' => 3600])) {
// 将域名添加到队列
RedisUtil::lPush("domain_queue", $v["domain"]);
}
}
这句代码的意思
$redis->setnx($lockKey, 1);
是 Redis 原生命令 SETNX
的 PHP 调用,意思是:
只有当指定的 key$lockKey
不存在时,才将其设置为1
。如果该 key 已存在,则什么都不做。
📌 用法解释:
setnx = set if not exists
(如果不存在就设置)返回值是:
1
:表示设置成功(key 原来不存在)0
:表示设置失败(key 已经存在)
💡 典型用途:实现分布式锁
$lockKey = 'lock:my_job';
if ($redis->setnx($lockKey, 1)) {
// 获取锁成功
// 设置过期时间,避免死锁
$redis->expire($lockKey, 10); // 锁有效期10秒
try {
// 执行业务逻辑
} finally {
// 释放锁
$redis->del($lockKey);
}
} else {
// 获取锁失败,可能其他进程正在执行
}
⚠️ 注意:
SETNX
本身 不支持设置过期时间 ,所以需要 单独调用 expire()
来避免锁永远不释放。
为了解决这个问题,Redis 后来引入了更强大的原子命令:
$redis->set($lockKey, 1, ['NX', 'EX' => 10]); // 同时实现 SETNX 和 10 秒过期
$redis->set($lockKey, 1, 'NX', 'EX', 10);
✅ 总结:
命令 | 含义 | 适用场景 |
---|---|---|
setnx | 如果 key 不存在就设置 | 分布式锁、单例执行 |
hSetNx | 如果哈希字段不存在就设置 | 哈希结构的数据存储 |
set ... NX EX | 原子操作 + 过期时间设置 | 推荐方式,安全高效 |