Predis使用事务执行两条命令,但其中一条失败
P粉030479054
P粉030479054 2023-09-13 17:55:41
0
1
636

描述错误

我使用redis列表来做一个限制器,它大多数时候都按预期工作,但最近我发现有一些键没有过期时间。理想情况下,我将值“rpush”到列表中,并在一个中设置过期时间交易,并且在交易开始之前我也使用“watch”。

重现

我本地环境没有复现这个bug,即使我使用jmeter批量请求相关api,比如1秒500个请求

版本:

预测:v2.1.2 PHP 7.4 Redis服务器5.0.10

代码示例

$redisClient->watch($key);
$current = $redisClient->llen($key);

// Transaction start
$tx = $redisClient->transaction();
if ($current >= $limitNum) {
    $redisClient->unwatch();
    return false;
} else {
  
    if ($redisClient->exists($key)) {
        $tx->rpush($key, $now);

        try {
             $replies = $tx->execute();
             return true;
        } catch (\Exception $e) {
            return false;
        }
    } else {
        // Using transaction to let rpush and expire to be an atomic operation
        $tx->rpush($key, $now);
        $tx->expire($key, $expiryTime);

        try {
             $replies = $tx->execute();
             return true;
        } catch (\Exception $e) {
            return false;
        }
    }
}

其他

这是我的本地 Redis 服务器中的预期操作

Redis 事务是原子的。原子意味着要么处理所有命令,要么不处理任何命令。因此,在我的情况下,一把钥匙应该有有效期。

P粉030479054
P粉030479054

全部回复(1)
P粉113938880

Redis 事务不是这样的原子事务。它们是原子的,因为在执行命令中的事务时,没有其他进程可以访问密钥空间。如果事务中的命令失败,则将执行后续命令并且不会回滚。

例如,让我们执行一个包含错误命令的事务:

127.0.0.1:6379> exists mylist
(integer) 0
127.0.0.1:6379> lpush mylist a b c
(integer) 3
127.0.0.1:6379> multi
OK
127.0.0.1:6379> rpop mylist
QUEUED
127.0.0.1:6379> sadd mylist d
QUEUED
127.0.0.1:6379> expire mylist 300
QUEUED
127.0.0.1:6379> exec
1) "a"
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) (integer) 1
127.0.0.1:6379> ttl mylist
(integer) 297

这里我们检查列表是否存在,并向其中添加一些初始项目。然后,在事务中,我们从列表中弹出一个项目,错误地尝试添加一个新项目,认为键 mylist 拥有一个集合,然后设置键 mylist。第一个和第三个命令成功,最后,mylist 设置了生存时间。第二个命令失败。为此,Redis 中没有内置回滚功能 - 您的应用程序需要通过 watch 命令使用乐观锁定...这是为了在您的事务获得之前检测其他进程更改您的事务想要更改的键对服务器的独占访问。它不是回滚机制。

详细信息:https://redis.io/docs/interact/transactions/

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!