iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0

Day 12 的結論是過期的 key 不一定馬上被刪掉。那如果刪不夠快,記憶體真的滿了會怎樣?

很多人以為 Redis 會自己清掉舊的然後撐下去。但預設不會,滿了就直接讓寫入報錯。

常用指令

  • CONFIG GET maxmemory(記憶體上限,0 是不設限)
  • CONFIG GET maxmemory-policy(滿了之後該用什麼淘汰策略)
  • CONFIG SET maxmemory 50mb(改上限,不用重開)
  • INFO stats 的 evicted_keys(累計被淘汰掉幾個 key)
  • OBJECT IDLETIME key / OBJECT FREQ key(這個 key 多久沒被碰、被碰得多熱)

先看預設值:

https://ithelp.ithome.com.tw/upload/images/20260925/20184209cGt6Pjdprp.png

0 是不設限,Redis 會一路吃到被作業系統殺掉。noeviction 是不淘汰,寫不下就報錯。

預設值會怎樣

給一個 50MB 的上限,再灌 10 萬筆 1KB 的資料進去。以下 redis-cli 都在 db1 操作:

CONFIG SET maxmemory 50mb

灌資料這兩行在主機的終端機下,不是在 redis-cli 裡面(V 是 1000 個 x,讓每一筆大約 1KB,內容不重要):

V=$(printf 'x%.0s' $(seq 1 1000))

for i in $(seq 1 100000); do echo "SET cache:$i $V"; done | docker exec -i redis30days redis-cli -n 1 --pipe

https://ithelp.ithome.com.tw/upload/images/20260925/20184209PSzyawW9fR.png

10 萬筆只寫進去 46,660 筆,剩下的 53,340 筆整排被拒絕:

https://ithelp.ithome.com.tw/upload/images/20260925/20184209dUnHLppqYe.png

INFO stats 的 evicted_keys 是 0,一個 key 都沒被清掉。Redis 寧可不收新的,也不會動已經存進去的東西。

八種策略其實是兩個維度

不用背,這邊就是「從誰裡面挑」乘上「怎麼挑」:

所有 key 只挑有設 TTL 的
最久沒用(LRU) allkeys-lru volatile-lru
最少被用(LFU) allkeys-lfu volatile-lfu
隨機挑 allkeys-random volatile-random
剩下 TTL 最短 — volatile-ttl

七種加上預設不淘汰的 noeviction,就是常聽到的那八種。

換成 allkeys-lru

資料不動、Redis 不重開,只改一個設定:

CONFIG SET maxmemory-policy allkeys-lru

再灌一次一樣的 10 萬筆:

https://ithelp.ithome.com.tw/upload/images/20260925/20184209CaitCOlBC4.png

https://ithelp.ithome.com.tw/upload/images/20260925/20184209fk3AXAaB2K.png

這次一筆都沒被拒絕,evicted_keys 從 0 變成 53,431。

同樣的資料、同樣裝不下,差別只在 Redis 是拒收新的還是偷偷丟掉舊的。兩邊裝得下的量幾乎一樣(46,660 對 46,637),上限擋的是總容量,不是筆數。

LRU 還是 LFU

兩個各自看一件事,而且看得到。先在 LRU 底下建兩個 key,隔二十幾秒只讀 hot

SET cold "iamcold"
SET hot "iamhot"
# 等二十幾秒,中間只讀 hot
GET hot

OBJECT IDLETIME hot    # 3
OBJECT IDLETIME cold   # 24

https://ithelp.ithome.com.tw/upload/images/20260925/20184209xtybgsthfr.png

OBJECT IDLETIME 就是 LRU 的判斷依據:多久沒被碰過。換成 LFU 之後同樣這兩個 key,讀 hot 十次:

CONFIG SET maxmemory-policy allkeys-lfu

OBJECT FREQ hot    # 6
OBJECT FREQ cold   # 0

https://ithelp.ithome.com.tw/upload/images/20260925/20184209i6tPefK70J.png

讀十次不是變 10,OBJECT FREQ 取的是對數,不是真的次數,而且久沒被讀會慢慢掉回來。

兩個各有各的盲點:

  • LRU 怕一次性的大量讀取:半夜跑報表把整批冷門商品掃過一遍,它們全變成「剛剛才用過」,真正的熱門反而被擠掉。
  • LFU 怕新東西:剛上架的商品次數要從頭累積,還沒紅起來就可能被丟掉。Redis 讓新 key 的 OBJECT FREQ 從 5 起跳而不是 0,就是在補這個洞。

踩雷

選了 volatile-*,但沒有任何 key 設過 TTL,行為等同 noeviction。 沒有候選人可以淘汰,就只能報錯。

清空重來,同樣 50MB、同樣灌 10 萬筆,這次用 volatile-lru:

FLUSHDB
CONFIG RESETSTAT                          # evicted_keys 歸零
CONFIG SET maxmemory-policy volatile-lru

結果是 errors: 53151、evicted_keys: 0,跟第一個實驗一模一樣。

https://ithelp.ithome.com.tw/upload/images/20260925/20184209N255RAjec4.png

原因在這裡:

https://ithelp.ithome.com.tw/upload/images/20260925/20184209vi7jVYJHVa.png

46,849 個 key,設了 TTL 的是 0 個。這個 expires 就是 Day 12 定期刪除抽樣的那個池子,volatile-* 挑要丟誰也是從這裡挑,一樣是空的。

把灌資料那行改成 SET cache:$i $V EX 600,設定一個字都不用改,errors 就會變 0。

Day 4 說每個快取 key 都要設 TTL,當時的理由是不設就永遠佔著記憶體。現在多一條:不設的話,選好的淘汰策略根本不會生效。

為什麼要在意

  • 淘汰跟過期是兩回事。 過期是自己設的時間到了,淘汰是還沒到期就被丟掉,而且沒有任何通知。
  • 選 allkeys-* 的話所有 key 都是候選人,跟快取擠在同一台的設定檔、白名單這種本來就該一直在的資料也會被丟掉。
  • 這兩個值預設是不設限、不淘汰,等於什麼都沒決定。 上線前一定要自己挑一個,現在偷懶不設,之後講快取雪崩的時候就會害到自己。

測完記得還原,不然後面幾天會莫名其妙寫不進去:

CONFIG SET maxmemory 0
CONFIG SET maxmemory-policy noeviction
FLUSHALL

明天

上面這些都還在記憶體裡,容器一重開就全沒了。明天來看 RDB 怎麼把整包資料拍成快照存到硬碟上,還有存檔的那一瞬間為什麼會卡住所有連線/images/emoticon/emoticon08.gif


上一篇
Day 12|Key 什麼時候被刪掉
下一篇
Day 14|RDB
系列文
從購物車到秒殺:30 天 Redis 高併發自學筆記 共 21 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言