iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0

常用指令

List 是一串有順序的字串,兩頭都可以推,也都可以拿。

  • LPUSH key value ...(從左邊推進去,回傳推完的長度)| p2 p1 p0
    https://ithelp.ithome.com.tw/upload/images/20260918/20184209nGCBVy681n.png

  • RPUSH key value ...(從右邊推)| p2 p1 p0 p3 p4 p5
    https://ithelp.ithome.com.tw/upload/images/20260918/20184209e34DUCpmnU.png

  • LRANGE key start stop(可以用 0 -1 代表全部)
    https://ithelp.ithome.com.tw/upload/images/20260918/20184209NBmCfz4lLY.png

  • LLEN key(有幾筆)
    https://ithelp.ithome.com.tw/upload/images/20260918/201842093FMHHsSUTQ.png

  • LPOP key(從左邊拿走一個)| p1 p0 p3 p4 p5
    https://ithelp.ithome.com.tw/upload/images/20260918/20184209yKPPfwhTDJ.png

  • RPOP key(從右邊拿走一個)| p1 p0 p3 p4
    https://ithelp.ithome.com.tw/upload/images/20260918/20184209uyI553dpgy.png

  • LTRIM key start stop(只留這一段,其他刪掉)| p1 p0 p3
    https://ithelp.ithome.com.tw/upload/images/20260918/20184209wF5EjEL2bR.png

  • LREM key count value(count 給 0 表示整條列表裡的都刪,回傳刪掉幾個)| p0 p3
    https://ithelp.ithome.com.tw/upload/images/20260918/20184209QftalczSbH.png

  • BRPOP key timeout(右邊沒東西就掛著等,timeout 單位是秒,給 0 表示無限等待)| p0
    https://ithelp.ithome.com.tw/upload/images/20260918/201842094AHAcHaGmc.png

最近瀏覽的 10 件商品

要照時間排、只留 10 筆,LPUSH + LTRIM 兩行就好:

LPUSH view:1 p10
LTRIM view:1 0 9

LTRIM key 0 9 是「只留 index 0 到 9」,第 11 筆以後直接砍掉。每看一件就推一次、砍一次,長度永遠停在 10。

但同一件商品看兩次會出現兩筆,List 不會幫你去重,要先把舊的那筆拿掉再推:

LREM view:1 0 p10
LPUSH view:1 p10

Java 是用 ListOperations:

private final ListOperations<String, String> list;

public ViewController(StringRedisTemplate redis) {
    this.list = redis.opsForList();
}

/** 看了一件商品:看過就先拿掉,再推到最前面,最後砍掉第 11 筆以後 */
@PostMapping("/{userId}/{productId}")
public List<String> view(@PathVariable Long userId, @PathVariable String productId) {
    String key = "view:" + userId;
    list.remove(key, 0, productId); // count 0 = 整條列表裡的都刪掉
    list.leftPush(key, productId);
    list.trim(key, 0, 9);           // 只留 index 0~9
    return list.range(key, 0, -1);
}

流程:

先用 redis-cli 塞了 12 件進去,再 curl http://localhost:8080/view/1 得到:
https://ithelp.ithome.com.tw/upload/images/20260918/20184209xzKIwqNXw5.png

當我再次查看 10 號商品時 curl -X POST http://localhost:8080/view/1/p10,10 號商品會被加入到最前面並且只會留下最後看的 10 件商品:
https://ithelp.ithome.com.tw/upload/images/20260918/20184209XDISJyg2MO.png

當 Queue 用

左邊推、右邊拿,就是先進先出(FIFO):

LPUSH queue:order order:1   # 生產者
RPOP queue:order            # 消費者

問題是 RPOP 撈不到就回 nil,消費者只能隔一秒再問一次。沒訂單時一直空轉,有訂單又要等到下一次才拿得到。

BRPOP 就是為了這個來的,空的話直接掛在那裡等,有東西進來馬上回。

/** 換成 BRPOP:空的就掛在這裡等,有訂單進來馬上回,最多等 10 秒 */
@GetMapping("/take")
public Map<String, Object> take() {
    long start = System.currentTimeMillis();
    String order = list.rightPop(KEY, Duration.ofSeconds(10)); // 空的話這行會卡住,最多 10 秒
    return Map.of(
            "order", order == null ? "(等了 10 秒還是沒有)" : order,
            "waitedMs", System.currentTimeMillis() - start);
}

拿 List 當 MQ 的代價

RPOP 把訂單拿走的那一刻,Redis 裡就沒有這筆了。消費者要是處理到一半掛掉,這筆訂單不會有人再處理,也沒有任何地方記得它存在過。

MQ 該有的東西 List 都沒有:

  • 沒有 ACK:拿走就算結束,不會等你回報處理成功
  • 沒有重試:失敗只能自己塞回去,塞回去順序還會跑掉
  • 沒有消費者群組:一筆訊息只有一個人拿得到,做不到同一批訂單同時餵給好幾組服務

所以 List 適合的是「掉了也還好」的工作,像寄通知信、寫 log。真的不能掉的,之後遇到的 Stream 有 ACK 也有消費者群組,再嚴格就應該要去用 RabbitMQ 或 Kafka。

LRANGE key 0 -1 對幾十萬筆的 List 會卡住整個 Redis,跟 Day 3 的 KEYS * 是同一個問題,要分段撈。

相關範例程式碼可以參考 https://github.com/gary880306/redis-30days/tree/dev

明天

去重那段是先 LREM 再 LPUSH,因為 List 根本不管有沒有重複。明天的商品標籤就不該重複,而且還要能取交集(ex: 同時是「3C」又是「特價」的商品),繼續來看 Set 該怎麼做/images/emoticon/emoticon08.gif


上一篇
Day 5|購物車:String 還是 Hash?
下一篇
Day 7|Set
系列文
從購物車到秒殺:30 天 Redis 高併發自學筆記 共 21 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言