Redis鍵不會自動過期 - Ably

banq發表於2022-01-28

Ably 是一個釋出/訂閱訊息傳遞的平臺。釋出是在命名頻道上進行的,訂閱給定頻道的客戶端會將該頻道上的所有訊息傳遞給他們。我們使用Redis,一個用於基於金鑰儲存的分散式記憶體資料庫,來儲存各種實體,例如身份驗證令牌和臨時通道狀態。它非常適合在我們處理訊息時臨時儲存它們。

在任何給定時間,我們都有數十億個活動的 Redis 金鑰,這些金鑰分散在眾多 Redis 例項中。分片策略將相關鍵放在同一個分片中,以便我們可以執行原子更新相關鍵的操作。我們廣泛使用 Lua Redis 指令碼來查詢和更新鍵,並依靠指令碼執行的原子性來保持相關鍵值的完整性。也就是說,指令碼中的所有命令都執行,或者根本不執行,並且沒有其他命令同時執行。

我們還廣泛使用過期金鑰;Ably 服務的本質是頻道的大部分狀態都是短暫的,並且僅保留有限的時間段(通常為 2 分鐘)。我們將金鑰設定為具有 TTL,以便它們自動過期。

 

問題

一組相關鍵key的完整性要求所有鍵都存在或不存在。我們曾假設指令碼執行的原子性質也適用於指令碼呼叫的過期操作,但實際上,在同一個指令碼中實現失效過期多個鍵也會保持這種完整性?這並不是真的。

雖然過期操作在同一個指令碼中自動執行(沒有機會發生干預操作),但與每個過期操作相關的時間戳不一定相同。

執行TIME顯示兩個不同的值:

-- time.lua       

local a = redis.call('time')       
local b = redis.call('time')       
return {a, b}       
$ ./redis-cli --eval /app/time.lua      

1) 1) "1638280442"     
   2) "996960"     
2) 1) "1638280442"     
   2) "996966"      

檢查實際到期時間:

-- expire_check.lua     

redis.call('set', 'foo', '1')     
redis.call('expire', 'foo', 1)     

-- slow calls...

redis.call('set', 'bar', '2')     
redis.call('expire', 'bar', 1)     

local fooExpiry = redis.call('PEXPIRETIME', 'foo')     
local barExpiry = redis.call('PEXPIRETIME', 'bar')     
return {fooExpiry, barExpiry}     
$ ./redis-cli --eval /app/expire_check.lua     

1) (integer) 1638280843717     
2) (integer) 1638280843730     

過期可能不是精確的,它可能在 0 到 1 毫秒之間。

這意味著有時某些金鑰已過期,但其他相關金鑰尚未過期,這可能導致狀態不一致。

 

我們的解決方案

解決方案是使用EXPIREAT為所有相關鍵key設定絕對到期時間,而不是依賴於通過 TTL 的相對到期時間。

EXPIREAT如果鍵具有相同的設定,是否保證同時發生多個鍵到期,Redis 文件不清楚。為謹慎起見,我們重新排序了鍵key到期時間,以確保無論如何我們都避免不一致。

-- expire_new.lua     

-- Unix time     

local now = redis.call('time')[1]     
local expiry = now + 1     
redis.call('set', 'foo', '1')     
redis.call('expireat', 'foo', expiry)     

-- slow calls...     

redis.call('set', 'bar', '2')     
redis.call('expireat', 'bar', expiry)     
local fooExpiry = redis.call('PEXPIRETIME', 'foo')     
local barExpiry = redis.call('PEXPIRETIME', 'bar')     
return {now, fooExpiry, barExpiry}     
$ ./redis-cli --eval /app/expire_new.lua

2) (integer) 1638281266000     
3) (integer) 1638281266000     

 

相關文章