Predis使用事務執行兩個指令,但其中一個失敗
P粉030479054
P粉030479054 2023-09-13 17:55:41

描述錯誤

我使用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學習者快速成長!