我使用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/