redis expire方式設置緩存時間的坑

最近入職後一直很忙,今天國慶值班,把前段時間遇到的問題拋出來總結一下。

1、背景

在項目中需要記錄用戶的相關狀態,這些狀態會在一天內清除,且又不止0/1這兩種狀態,於是考慮將其放在一個hash的key當中。

但是redis對於hash類型,並沒有提供直接設置超時時間的支持,於是設計採用expire一個key的過期的時間方式來實現當日過期。

2、問題

通過觀察發現,隔天的數據,在共用一個key的情況下並沒有如預估的一樣過期。

3、原因

因爲所有的hash都共用一個key,比如hermes:newest,這樣當設置了hash的field之後,會緊接着通過expire設置過期時間,如

// 代碼經過改寫過,大意就是這樣,把公司的包替換了
template.execute(jedis -> {
    Pipeline pipeline = bedis.pipelined();
    pipeline.hset(keyHash, String.valueOf(uid), JsonUtils.toJson(userInfo));
    pipeline.expire(keyHash, seconds);
    return pipeline.sync(false);
});

A、過期時間是通過計算到下一天0點時的剩餘時間。

B、expire的刪除方式爲定期刪除+懶刪除,不一定會立即刪除掉這個key。每次從數據庫獲取key的時候去檢查是否過期,若過期,則刪除,返回null。或者定期掃描去刪除。

這樣一來,當11點59分59秒設置了失效時間爲1秒時,再接下來00點00分00秒時,設置的失效時間並不是0秒,而是到後一天的24小時,所以會在刪除前,直接覆蓋掉之前的key的過期時間,使前一天的field結果不被刪除。

4、解決方法

A、採用定時任務,每天0點對這個key做一次主動刪除操作。

B、hash的key上帶有時間標記,如加上每一天,這樣就會使得key的過期時間不被覆蓋,到了定時刪除的時間,前一天的key自然會被刪除。

C、採用setx的方式來存儲key-value

目前在項目中採用的爲B方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章