Redis Hands-On
1. 事前準備
1.1. Redisコンテナーの起動 (初回起動の場合)
shell
docker run -d --name redis-test -p 6379:6379 redis:latest
1.2. Redisコンテナーの起動 (前回起動した同じコンテナーを利用する場合)
shell
! docker start redis-test
1.3. Hands-On前にRedisの中身をクリーニングする
以下をnotebookのセルで実行
notebook
r.flushdb()
output
True
2. Redisの基本動作の確認
2.1. 文字列操作: キーバリューの設定/取得/上書きの動作の確認
Redisの基本的なキーバリューの「設定 (Set)」「取得 (Get)」「上書き」の動作を確認する
-
set
:-
language
という名前のキーに、最初にPython
という値を設定 -
language
キーの値を 'Rust' に上書きも可能
-
-
get
:- 設定した値の読み出し
notebook
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# SetとGet
r.set('language', 'Python')
print(r.get('language').decode()) # → Python
# 文字列の上書き
r.set('language', 'Rust')
print(r.get('language').decode()) # → Rust
output
Python
Rust
2.2. List: リストにデータを順番に格納/取り出しの実施
-
lpush
:-
fruits
という名前の「順番付きのデータ入れ(リスト)」を用意する - そのリストの先頭に
apple
、次にbanana
、最後にorange
を順番に押し込んでいく - 結果、リストの中は先頭から
['orange', 'banana', 'apple']
という並びになる
-
-
lrange
:-
fruits
リストの中身を最初から最後まで全部取り出して画面に表示する -
0
は最初の要素 -
-1
は最後の要素という意味
-
notebook
# LPUSHでリストに値を追加
r.lpush('fruits', 'apple', 'banana', 'orange')
# LRANGEで全要素を取得(0から-1まで)
print(r.lrange('fruits', 0, -1)) # → [b'orange', b'banana', b'apple']
output
[b'orange', b'banana', b'apple']
2.3. Set: セット型データを使って重複させたくないデータを管理
-
sadd
:-
colors
という名前の「順番がなく、同じものは入らない特別な袋(セット)」を用意 - その袋に
red
,blue
,green
という色と、もう一度red
を入れようとする - でも、同じものは入らないルールなので、袋の中には
red
,blue
,green
がそれぞれ1つずつだけ入るはず
-
-
smembers
:-
colors
袋の中身を全部(順番は気にせず)取り出して、画面に表示する - このとき重複がなく、各要素はユニークとなっている
-
notebook
# SADDでセットに追加(重複は無視される)
r.sadd('colors', 'red', 'blue', 'green', 'red')
# SMEMBERSですべて取得(順不同)
print(r.smembers('colors')) # → {b'red', b'green', b'blue'}
output
{b'blue', b'red', b'green'}
2.4. Hash: 1つのハッシュに複数の情報を項目ごとに整理して保存できる
-
hset
:-
user:1
という名前の「プロフィールカード(ハッシュ)」を用意する - そのカードに、
name
という項目にはAlice
、age
という項目には30
と書き込む
-
-
hget
:- 次に、そのカードから
name
の項目だけを取り出して表示する(→Alice
と表示される)
- 次に、そのカードから
-
hgetall
:- 最後に、そのカードに書かれている「すべての項目とその内容」をまとめて取り出して表示する
notebook
# HSETでフィールドをセット
r.hset('user:1', mapping={'name': 'Alice', 'age': 30})
# HGETで1つのフィールドを取得
print(r.hget('user:1', 'name').decode()) # → Alice
# HGETALLで全フィールド取得
print(r.hgetall('user:1')) # → {b'name': b'Alice', b'age': b'30'}
output
Alice
{b'name': b'Alice', b'age': b'30'}
2.5. Sorted Set: 値が低い順もしくは高い順にデータを取り出し
-
zadd
:-
ranking
という名前の「得点付きランキングボード(ソート済みセット)」を作る - ランキングボードに、「
Alice
さんは100
点」「Bob
さんは150
点」「Carol
さんは120
点」と登録する - Redisが内部で自動的にスコアの低い順に並べ替える準備をしてくれる
-
-
zrange
:- ランキングボードのメンバー全員を、スコアが低い順に、もしくはスコアが高い順にスコアといっしょに取り出して画面に表示する
- スコアが高い順にする場合は引数に
desc=True
を用いる
notebook
# ZADDでスコア付きデータ追加
r.zadd('ranking', {'Alice': 100, 'Bob': 150, 'Carol': 120})
# ZRANGEでソートされた順に取得(withscores=Trueでスコア付き)
print(r.zrange('ranking', 0, -1, withscores=True))
# → [(b'Alice', 100.0), (b'Carol', 120.0), (b'Bob', 150.0)]
# ZRANGEでソートされた逆順に取得(withscores=Trueでスコア付き, desc=Trueで逆順)
print(r.zrange('ranking', 0, -1, withscores=True, desc=True))
# → [(b'Alice', 100.0), (b'Carol', 120.0), (b'Bob', 150.0)]
output
[(b'Alice', 100.0), (b'Carol', 120.0), (b'Bob', 150.0)]
[(b'Bob', 150.0), (b'Carol', 120.0), (b'Alice', 100.0)]
3. Listを使ったTodoリストの応用例
念のため環境のクリーン
notebook
r.flushdb()
3.1. 新しいタスクを追加(LPUSH
)
notebook
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# 新しいタスクを追加
r.lpush('todo', '買い物に行く', 'メール返信', 'Redisを学ぶ')
3.2. タスク一覧を表示(LRANGE
)
notebook
# 現在のタスク一覧
print('Todoリスト:')
for task in r.lrange('todo', 0, -1):
print('-', task.decode())
output
Todoリスト:
- Redisを学ぶ
- メール返信
- 買い物に行く
3.3. 完了したタスクを削除(RPOP
)
-
LPUSH
でタスクを追加した場合、RPOP
は「一番古い情報(最初に追加されたタスク)」を取り出すことになる - これは、お店の行列と同じで、「最初に来た人 (First-In) が最初にサービスを受ける (First-Out)」なので、FIFO(ファイフォ)キューとなる
notebook
# タスクを完了(末尾から削除)
done = r.rpop('todo')
print('完了タスク:', done.decode())
output
完了タスク: 買い物に行く
3.4. 削除確認
notebook
# 更新後のタスク一覧
print('更新後のTodoリスト:')
for task in r.lrange('todo', 0, -1):
print('-', task.decode())
買い物に行く
が削除されていることを確認
output
更新後のTodoリスト:
- Redisを学ぶ
- メール返信
4. ユーザー情報を管理(Hash)
念のためRedisの初期化を実施
notebook
r.flushdb()
4.1. ユーザー情報(名前、年齢、メール)を登録(HSET
)
notebook
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# ユーザー情報の登録
user_id = 'user:1001'
r.hset(user_id, mapping={
'name': 'Taro',
'age': '25',
'email': 'taro@example.com'
})
4.2. 個別の情報取得(HGET
)
notebook
# 個別に取得
print('名前:', r.hget(user_id, 'name').decode())
output
名前: Taro
4.3. 全情報取得(HGETALL
)
notebook
# すべて取得
print('ユーザー情報:')
for key, value in r.hgetall(user_id).items():
print(f"{key.decode()}: {value.decode()}")
output
ユーザー情報:
name: Taro
age: 25
email: taro@example.com
4.3. RedisInsightでの内容確認
5. 人気ランキング(Sorted Set)
5.1. ユーザーのスコアを追加(ZADD
)
notebook
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
# スコアを追加(スコアは数値)
r.zadd('ranking', {
'Taro': 150,
'Hanako': 180,
'Ken': 120
})
5.2. ランキング表示(ZRANGE
)
notebook
# ランキング表示(スコア付き)
print('人気ランキング:')
for name, score in r.zrange('ranking', 0, -1, withscores=True):
print(f"{name.decode()}: {int(score)}")
output
人気ランキング:
Ken: 120
Taro: 150
Hanako: 180
6. Redis Pub/Sub とは?
Pub/Subの意味
-
Publisher
: メッセージを「チャンネル」に送る人(例:通知を送る側) -
Subscriber
: チャンネルを購読してメッセージを受け取る人(例:通知を受け取る側)
応用例
- Flask + WebSocket + Redis でチャットアプリのバックエンドに
- ログ監視やイベント通知システムに活用
- IoT デバイスのイベント通知にもよく使われます
6.1. Subscriber(受け取る側)のコードを保存する
以下の内容を subscriber.py
という名前で保存する
subscriber.py
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
pubsub = r.pubsub()
# 'news' チャンネルを購読
pubsub.subscribe('news')
print("チャンネル 'news' を購読中...")
for message in pubsub.listen():
if message['type'] == 'message':
print("受信:", message['data'].decode())
6.2. Publisher(送る側)
以下の内容を publisher.py
という名前で保存する
publisher.py
import redis
import time
r = redis.Redis(host='localhost', port=6379, db=0)
messages = [
"Redisの練習を始めよう!",
"新しいタスクが追加されました",
"通知:サーバーが起動しました",
]
for msg in messages:
r.publish('news', msg)
print("送信:", msg)
time.sleep(1)
6.3. terminal1を開いて、以下のコマンドを実行する
$ python subscriber.py
チャンネル 'news' を購読中...
6.4. terminal2を開いて、以下のコマンドを実行する
$ python publisher.py
送信: Redisの練習を始めよう!
送信: 新しいタスクが追加されました
送信: 通知:サーバーが起動しました
6.5. terminal1の表示を観察する
受信: Redisの練習を始めよう!
受信: 新しいタスクが追加されました
受信: 通知:サーバーが起動しました
7. Clean Up
6.1. Redisコンテナーの停止
terminal
docker stop redis-test