Redis Lists(パート1)

Listは順序付けられた、重複可能な文字列のシーケンスです。このレッスンでは、基本的なリスト操作について解説します。

Listとは

Listは以下のものに似ています:

Redisのリストの特徴:

mylist
├─ "element1"
├─ "element2"
├─ "element3"
└─ "element4"
💡 内部実装: Redisのリストはquicklistを使用しています。これは双方向リンクリストとziplistの組み合わせで、メモリとパフォーマンスのバランスを取ります。

LPUSHとRPUSH:要素の挿入

LPUSH:左側(先頭)から挿入

REDIS
# 左側から単一の要素を挿入
LPUSH mylist "world"
(integer) 1  # リストの長さを返す

# 左側から複数の要素を挿入
LPUSH mylist "hello"
(integer) 2

# リストを表示
LRANGE mylist 0 -1
1) "hello"
2) "world"
⚠️ 補足: LPUSHで複数の要素を挿入する場合、パラメータの順に左から右へ挿入されます。LPUSH mylist a b c の結果は [c, b, a, ...] となります。

RPUSH:右側(末尾)から挿入

REDIS
# 右側から挿入
RPUSH mylist "Redis"
(integer) 3

RPUSH mylist "Tutorial"
(integer) 4

# リストを表示
LRANGE mylist 0 -1
1) "hello"
2) "world"
3) "Redis"
4) "Tutorial"

LPUSHXとRPUSHX:リストが存在する場合のみ挿入

REDIS
# リストが存在しない場合は挿入失敗
LPUSHX notexist "value"
(integer) 0

# リストが存在する場合は挿入成功
LPUSHX mylist "first"
(integer) 5

RPUSHX mylist "last"
(integer) 6

LPOPとRPOP:要素のポップ

LPOP:左側(先頭)からポップ

REDIS
# 現在のリストを表示
LRANGE mylist 0 -1
1) "first"
2) "hello"
3) "world"
4) "Redis"
5) "Tutorial"
6) "last"

# 左側からポップ
LPOP mylist
"first"

# 複数の要素をポップ(Redis 6.2+)
LPOP mylist 2
1) "hello"
2) "world"

RPOP:右側(末尾)からポップ

REDIS
# 右側からポップ
RPOP mylist
"last"

# 複数の要素をポップ(Redis 6.2+)
RPOP mylist 2
1) "Tutorial"
2) "Redis"

リストが空の場合

REDIS
LPOP emptylist
(nil)  # 空のリストはnilを返す

LLEN:リストの長さを取得

REDIS
LPUSH mylist "a" "b" "c"
LLEN mylist
(integer) 3

# リストが存在しない場合
LLEN notexist
(integer) 0

LRANGE:範囲内の要素を取得

LRANGEは最も一般的なリストクエリコマンドです。

基本的な使用方法

REDIS
LPUSH mylist "one" "two" "three" "four" "five"

# すべての要素を取得
LRANGE mylist 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
5) "one"

# 最初の3つの要素を取得
LRANGE mylist 0 2
1) "five"
2) "four"
3) "three"

# 最後の2つの要素を取得
LRANGE mylist -2 -1
1) "two"
2) "one"

# インデックス1から3の要素を取得
LRANGE mylist 1 3
1) "four"
2) "three"
3) "two"

インデックスルール

インデックス 説明
0 最初の要素
1 2番目の要素
-1 最後の要素
-2 最後から2番目の要素
0 -1 すべての要素
⚠️ 補足: LRANGEの終了インデックスは含まれます。LRANGE 0 2 はインデックス0、1、2の要素を返します(合計3要素)。

Listを使用したキューとスタックの実装

キュー(FIFO:先入れ先出し)

RPUSH + LPOPを使用:

REDIS
# プロデューサー:右側からエンキュー
RPUSH queue:task "task1"
RPUSH queue:task "task2"
RPUSH queue:task "task3"

# コンシューマー:左側からデキュー
LPOP queue:task
"task1"

LPOP queue:task
"task2"
エンキュー → [task1, task2, task3] → デキュー
          RPUSH               LPOP

スタック(LIFO:後入れ先出し)

LPUSH + LPOPを使用:

REDIS
# プッシュ
LPUSH stack:data "item1"
LPUSH stack:data "item2"
LPUSH stack:data "item3"

# ポップ
LPOP stack:data
"item3"

LPOP stack:data
"item2"
プッシュ → [item3, item2, item1] → ポップ
       LPUSH                LPOP
💡 覚え方:

  • キュー:RPUSH + LPOP(右から入れて左から出す)
  • スタック:LPUSH + LPOP(左から入れて左から出す)

ブロッキングポップ:BLPOPとBRPOP

リストが空の場合、BLPOPとBRPOPは要素が利用可能になるか、タイムアウトが発生するまでブロックします。

BLPOP:ブロッキング左ポップ

REDIS
# クライアント1:キューでタスクを待つ(最大10秒)
BLPOP queue:task 10

# クライアント2:タスクを追加
RPUSH queue:task "new_task"

# クライアント1が受信:
1) "queue:task"  # リスト名
2) "new_task"    # ポップされた要素

BRPOP:ブロッキング右ポップ

REDIS
BRPOP queue:task 10

複数リストでのブロッキング

REDIS
# 複数のリストで待機、最初にデータがあるものを返す
BLPOP queue:high queue:low 10
💡 使用例: BLPOP/BRPOPはタスクキューやメッセージキューの実装によく使用され、コンシューマーが新しいタスクを待ってブロックします。

Listのユースケース

ユースケース1:メッセージキュー

REDIS
# プロデューサー:メッセージを送信
RPUSH queue:email '{"to":"user@example.com","subject":"Hello"}'

# コンシューマー:メッセージを受信(ブロッキング待機)
BLPOP queue:email 0

ユースケース2:最新リスト

REDIS
# 最新の記事を追加
LPUSH articles:latest "article:123"
LPUSH articles:latest "article:124"
LPUSH articles:latest "article:125"

# 最新の10記事を取得
LRANGE articles:latest 0 9

# リストを最大100記事に制限
LTRIM articles:latest 0 99

ユースケース3:タイムライン

REDIS
# 投稿を公開(ユーザータイムラインに追加)
LPUSH timeline:user:1 "post:1001"
LPUSH timeline:user:1 "post:1002"

# タイムラインを表示(最新20件)
LRANGE timeline:user:1 0 19

ユースケース4:操作ログ

REDIS
# ユーザーアクションを記録
RPUSH log:user:1 "login at 2026-06-23 10:00:00"
RPUSH log:user:1 "view article:123"
RPUSH log:user:1 "like article:123"

# 最近のアクションを表示
LRANGE log:user:1 -10 -1

LTRIM:リストのトリミング

LTRIMは指定された範囲内の要素を保持し、それ以外を削除します。

REDIS
LPUSH mylist "one" "two" "three" "four" "five"

# 最初の3要素のみ保持
LTRIM mylist 0 2
OK

LRANGE mylist 0 -1
1) "five"
2) "four"
3) "three"

使用例:固定サイズのリストを維持

REDIS
# 新しい記事を追加し、最大100件に制限
LPUSH articles:latest "article:new"
LTRIM articles:latest 0 99
💡 ヒント: LPUSH + LTRIM を組み合わせると、最新アイテムの上限付きリストを維持できます。

❓ よくある質問

Q Listの最大要素数は?
A 最大2^32 - 1(約42億)ですが、実際にはメモリによって制限されます。
Q LPUSHとRPOPの速度は?
A O(1)の時間複雑度で、非常に高速です。先頭と末尾の操作は定数時間です。
Q LRANGEの速度は?
A O(N)で、Nは返される要素数です。広い範囲を取得すると遅くなります。
Q BLPOPは無期限にブロックしますか?
A いいえ。タイムアウトを設定してください。タイムアウト後はnilを返します。タイムアウト0は永久待機を意味します。
Q リストの途中に挿入するには?
A LINSERTを使用しますが、O(N)のパフォーマンスです。先頭と末尾のみで操作することを推奨します。

📖 まとめ

📝 練習問題

  1. キューの実装: RPUSHとLPOPを使用してタスクキューを実装し、プロデューサーとコンシューマーをシミュレートしましょう
  2. スタックの実装: LPUSHとLPOPを使用してスタックを実装し、プッシュとポップをシミュレートしましょう
  3. 最新リスト: 最新記事リストを実装し、記事を追加して最大10件に制限しましょう
  4. ブロッキング待機: BLPOPを使用してブロッキングタスクキューを実装しましょう(2つのターミナルが必要)

次のレッスン

次のレッスンでは、Redis Lists(パート2)について学びます。クエリ、変更、高度なリスト操作を解説します。

100%