webman 的 redis 库 Redis::setNx 方法不是原子性的吗?

lscho

背景描述

部署环境为分布式部署,定时任务期望其中某台服务器执行,故在代码中使用 Redis::setNx 来创建任务锁,期望拿到锁的服务器执行任务。

示例代码

        // 每天凌晨2点执行更新状态信息
        new Crontab('0 2 * * *', function () {
            // 负载均衡下有多台服务器,所以拿到更新锁的服务器才能执行任务
            $lock = Redis::setNx('update_status_' . date('Y-m-d'), 1);
            if ($lock) {
                // 执行更新任务
            }
        });

结果

本地测试没有问题,但是在服务器中,多台服务器都会执行更新任务,请问问题出在哪里?

结帖

感谢大佬们帮助,问题找到了。上次发版拉配置中心的配置,拉redis的配置失败了,导致走了默认的配置,应用服务器都连到自己所在服务器的redis了。

1130 7 2
7个回答

admin

Redis 的 setNx 操作实现确实是原子性的,可以保证在并发场景下正确的实现资源锁定,但是在分布式场景下,多个服务器同时连接 Redis 数据库,不同服务器之间存在网络延迟等问题,可能导致 Redis 数据库中的锁被多个服务器同时获取。因此,即使使用 Redis 的 setNx 操作也无法保证在分布式环境下的互斥访问。

为解决这类问题,我们可以采用分布式锁的方式,例如基于 ZooKeeper 或 Redis 实现的分布式锁。这样可以保证分布式环境下的互斥访问,并且可以实现高可用性。具体来说,分布式锁需要保证:

  • 唯一性。同一时刻只能有一个进程持有锁。
  • 可重入性。同一个进程可以重复获取锁。
  • 超时机制。当持有锁进程出现异常情况(如网络故障、进程崩溃等),锁应具有自动释放能力,防止资源被一直占用。

还有就是试试 redis集群自带的key的分片,分片到指定的redis服务器,这样可能就不会造成多个服务器拿到锁。

这是我自己想想的,没有经过实践,如解决了就更好了

  • pader 2023-08-03

    你这是什么回答?setnx 可以保证原子性,即使存在网络延迟也不影响这一点,简单的防并发用 setnx 完全没有问题。你这看着像 ChatGPT 回答的。

  • 小W 2023-08-03

    按照上面说的,是不是会出现死锁吧

yongdao35

看下用的redis是否同一个配置,并且db相同,每台服务器系统的日期设置是否正确

  • 暂无评论
yzh52521

https://www.workerman.net/plugin/42
你可以看下我这个插件的源码

  • kaka1915 2023-08-01

    我看你的介绍有后台界面。为啥我下载下来了没有呢 就是你展示的效果图那种

  • yzh52521 2023-08-01

    界面是自己项目里的ui

  • kaka1915 2023-08-01

    大佬 没有看到有sql文件呢

  • yzh52521 2023-08-02

    在源码里,运行的时候就创建好了表

ichynul

你这锁有点简单了,建议用开源的,不要自己实现。
虽然没看到你解锁的代码。但可以猜测是用Redis::del()吧。
解锁还是会有问题的。
推荐你用我这个:https://github.com/ichynul/php-redis-lock

  • 暂无评论
smile1

如果是分布式的场景下,使用redis ,是不是只有指定到固定的节点去拿锁和加锁呀,要不然就是伪概念红锁来处理

  • 暂无评论
qq7467466

搞那么多乱七八糟的, 分布式场景下推荐用这个锁

https://github.com/ronnylt/redlock-php

  • 暂无评论
efnic

推荐用这个https://www.workerman.net/plugin/55
基于symfony/lock的,质量有保证。

  • 暂无评论
🔝