我使用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 事务是原子的。原子意味着要么处理所有命令,要么不处理任何命令。因此,在我的情况下,一把钥匙应该有有效期。
Redis 事务不是这样的原子事务。它们是原子的,因为在执行命令中的事务时,没有其他进程可以访问密钥空间。如果事务中的命令失败,则将执行后续命令并且不会回滚。
例如,让我们执行一个包含错误命令的事务:
这里我们检查列表是否存在,并向其中添加一些初始项目。然后,在事务中,我们从列表中弹出一个项目,错误地尝试添加一个新项目,认为键
mylist
拥有一个集合,然后设置键mylist。第一个和第三个命令成功,最后,
mylist
设置了生存时间。第二个命令失败。为此,Redis 中没有内置回滚功能 - 您的应用程序需要通过watch
命令使用乐观锁定...这是为了在您的事务获得之前检测其他进程更改您的事务想要更改的键对服务器的独占访问。它不是回滚机制。详细信息:https://redis.io/docs/interact/transactions/