Redis 入門と演習(データ構造・キー設計・Python からの操作)
【概要】
Redis(レディス)の基本的なデータ構造、キー設計パターン、キャッシュ戦略を学び、Pythonと redis-py(Redis の Python クライアント)を用いた具体的な操作を演習する。2026 年時点の最新安定版は Redis 8 系であり、検索機能(Redis Search)などが本体に統合されている。本資料の内容は Redis 7 系以降で動作する。
【目次】
【関連する外部ページ】
【サイト内の関連ページ】
Redis 入門
- Redis の基本的なデータ構造と活用例
- Strings(文字列型):セッションID(一時的なユーザー識別子)の保存
SET session:123 "user_data" - Hashes(ハッシュ型):ユーザープロフィール
HSET user:1000 name "田中" age "30" - Sets(集合型):グループメンバーシップ(所属関係)
SADD team:dev "user:1000" "user:1001" - Sorted Sets(順序付き集合型):ランキング機能
ZADD scores 100 "user:1000"
- Strings(文字列型):セッションID(一時的なユーザー識別子)の保存
- SQL ライクな操作の実現方法
- テーブル相当:
users:1000のようなキー設計でエンティティ(実体)を表現することができる - インデックス:
department:営業部のようなセットで検索用インデックス(索引)を実現することができる - 結合操作:パイプライン(複数操作の一括実行機能)を使用して複数のハッシュを取得し、アプリケーション層で結合することができる
- テーブル相当:
- 効率的なキー設計パターン
- エンティティ分類:
<エンティティ名>:<ID>:<属性>の形式で一意性を確保する - インデックス用:
<インデックス名>:<値>の形式で検索効率を向上させる - 有効期限付きデータ:
temp:<データ種別>:<ID>の形式で一時データを管理する
- エンティティ分類:
- 実用的なキャッシュ戦略
# Cache-Aside Pattern(キャッシュサイドパターン:データの効率的な読み書き方式) def get_data(key): cache = redis.get(key) if cache: return cache data = db.query(key) # メインDBから取得 redis.setex(key, 3600, data) # 1時間のTTL(Time To Live:生存時間)で保存 return data - データ整合性を保つための実装パターン
# トランザクション(不可分な処理のまとまり)を使用した更新 pipe = redis.pipeline() pipe.hset("users:1000", "department", "技術部") # ユーザーの部署を更新 pipe.srem("department:営業部", "1000") # 旧所属からの削除 pipe.sadd("department:技術部", "1000") # 新所属への追加 pipe.execute() # トランザクションの実行 - メモリ管理のベストプラクティス
- TTL(Time To Live:生存時間)の設定:
EXPIRE users:1000 3600 - 大きなハッシュの分割:フィールド数が多いハッシュは別キーに分割することでメモリ効率を向上させる
- 期限切れキーの走査:
SCANコマンドを使用してキーを反復的に走査する
- TTL(Time To Live:生存時間)の設定:
- 高度な検索機能(Redis Search)の活用
Redis 8 以降では、検索機能(Redis Search)が本体に統合されている。
# インデックス作成 FT.CREATE users:index ON HASH PREFIX 1 users: SCHEMA name TEXT age NUMERIC department TEXT # 複合条件での検索(部署が技術部かつ年齢が20から30の範囲) FT.SEARCH users:index "@department:技術部 @age:[20 30]" - スケーラビリティ対策
- Redis Cluster(分散構成)の利用:データを複数のノードに分散して格納する
- キーの分散配置:ハッシュスロットによるデータ分散を実現する
- 読み取り専用レプリカの活用:読み取り処理の負荷分散を実現する
これらの実装パターンは、アプリケーションの要件に応じて組み合わせることで、高速で信頼性の高いシステムを構築できる。
Redis 演習
学習目標と内容
Redis でのデータ操作方法を習得し、データ構造とインデックスの実装技術を理解する。環境設定と準備作業
Python から利用するため、redis-py を導入する。pip install -U --no-user redisRedis コマンドラインの起動と基本操作
起動手順:redis-cliコマンドを実行することで Redis のコマンドラインインタフェースを起動する。ヘルプ機能の利用方法:
helpコマンドを実行すると、利用可能なコマンドの案内が表示される。終了手順:
exitコマンドを実行することで、セッションを終了する。Python からの接続
Python から Redis を操作する前に、接続オブジェクトを作成する。
decode_responses=Trueを指定すると、応答がバイト列ではなく文字列で返る(初学者はこの指定を付けておくと扱いやすい)。以降のコードは、このredisオブジェクトが作成済みであることを前提とする。import redis from datetime import datetime redis = redis.Redis(host='localhost', port=6379, decode_responses=True)データの挿入手順
ここでは order(注文記録)データを登録する。Hash(ハッシュ型)を使用して構造化データを格納する。
【説明】Hash データモデルでレコードを保存する。
hsetのmapping引数でフィールドと値のペアを一括設定する(HMSETは Redis 4.0.0 以降で非推奨のため、HSETを使用する)。作成時刻には ISO 8601 形式のタイムスタンプを用いる。データの一貫性はアプリケーション層で担保する。【補足:基本的なデータ構造】
- Strings(文字列型):セッションID の保存
SET session:123 "user_data" - Hashes(ハッシュ型):ユーザープロフィール
HSET user:1000 name "田中" age "30" - Sets(集合型):グループメンバーシップ
SADD team:dev "user:1000" "user:1001" - Sorted Sets(順序付き集合型):ランキング機能
ZADD scores 100 "user:1000"
# データ挿入 redis.hset('order:1', mapping={ 'year': 2022, 'month': 10, 'day': 26, 'customer_name': 'kaneko', 'product_name': 'orange A', 'unit_price': 1.2, 'qty': 10, 'created_at': datetime.now().isoformat(), 'updated_at': '', }) redis.hset('order:2', mapping={ 'year': 2022, 'month': 10, 'day': 26, 'customer_name': 'miyamoto', 'product_name': 'Apple M', 'unit_price': 2.5, 'qty': 2, 'created_at': datetime.now().isoformat(), 'updated_at': '', }) redis.hset('order:3', mapping={ 'year': 2022, 'month': 10, 'day': 27, 'customer_name': 'kaneko', 'product_name': 'orange B', 'unit_price': 1.2, 'qty': 8, 'created_at': datetime.now().isoformat(), 'updated_at': '', }) redis.hset('order:4', mapping={ 'year': 2022, 'month': 10, 'day': 28, 'customer_name': 'miyamoto', 'product_name': 'Apple L', 'unit_price': 3.0, 'qty': 1, 'created_at': datetime.now().isoformat(), 'updated_at': '', })- Strings(文字列型):セッションID の保存
問い合わせの実行と結果確認
【説明】
scan_iter(SCANコマンドをラップしたイテレータ)で効率的に反復し、hgetallでハッシュ全体を取得する。パターンマッチングによるキーの走査と条件フィルタリングを組み合わせることで、目的のデータを検索する。SCANはカーソルベースの反復子であり、大量のキーがあってもサーバをブロックしにくい。【補足:キー設計パターン】
- エンティティ分類:
<エンティティ名>:<ID>:<属性> - インデックス用:
<インデックス名>:<値> - 有効期限付きデータ:
temp:<データ種別>:<ID>
# 全データの取得 for key in redis.scan_iter("order:*"): print(redis.hgetall(key)) # 特定の日付のデータ取得 for key in redis.scan_iter("order:*"): order = redis.hgetall(key) if order['day'] == '26': print(order) # 特定の顧客のデータ取得 for key in redis.scan_iter("order:*"): order = redis.hgetall(key) if order['customer_name'] == 'kaneko': print(order) # 特定の単価以上のデータ取得 for key in redis.scan_iter("order:*"): order = redis.hgetall(key) if float(order['unit_price']) > 2: print(order)- エンティティ分類:
データ更新の実践的手法
【説明】
hsetで複数フィールドを更新する。更新時刻をあわせて記録し、フィールド単位の部分更新を行う。必要に応じてトランザクション制御と組み合わせることができる。【補足:データ整合性を保つための実装パターン】
# トランザクションを使用した更新 pipe = redis.pipeline() pipe.hset("users:1000", "department", "技術部") pipe.srem("department:営業部", "1000") pipe.sadd("department:技術部", "1000") pipe.execute()# 数量と更新時刻の更新 redis.hset('order:1', mapping={ 'qty': 12, 'updated_at': datetime.now().isoformat(), })データ削除の方法
【説明】
deleteコマンドでキー単位に削除する。関連するインデックスの整合性を保つため、トランザクション処理と組み合わせて、セカンダリインデックスも同時に更新する。# 特定のデータの削除 redis.delete('order:2')インデックスの活用
【説明】Set(集合型)を使ってセカンダリインデックス(副次的な索引)を実装する。
saddでメンバーを追加し、smembersで逆引き検索を行う。キー設計パターンに基づき、目的のデータへ効率的にアクセスできる。# 顧客名によるインデックス作成 redis.sadd('customer:kaneko', 'order:1') redis.sadd('customer:kaneko', 'order:3') redis.sadd('customer:miyamoto', 'order:2') redis.sadd('customer:miyamoto', 'order:4') # インデックスを使用した検索 customer_orders = redis.smembers('customer:kaneko') for order_id in customer_orders: print(redis.hgetall(order_id)) # 日付によるインデックス作成 redis.sadd('day:26', 'order:1') redis.sadd('day:26', 'order:2') redis.sadd('day:27', 'order:3') redis.sadd('day:28', 'order:4') # 日付インデックスを使用した検索 day_orders = redis.smembers('day:26') for order_id in day_orders: print(redis.hgetall(order_id))データのバックアップとリストア
【説明】Redis の永続化機能を活用する。
SAVEコマンドはスナップショットを同期的に作成し、BGSAVE(Background SAVE)はバックグラウンドで非同期に作成する。SAVEは処理が終わるまでサーバの他の処理をブロックするため、通常はBGSAVEを用いる。AOF(Append Only File:追記専用ファイル)を有効にすると、書き込み操作のログを永続化できる。【補足:Cache-Aside Pattern 実装例】
def get_data(key): cache = redis.get(key) if cache: return cache data = db.query(key) # メインDBから取得 redis.setex(key, 3600, data) # 1時間のTTLで保存 return data【補足:Redis Search 活用例】
# インデックス作成 FT.CREATE users:index ON HASH PREFIX 1 users: SCHEMA name TEXT age NUMERIC department TEXT # 複合条件での検索 FT.SEARCH users:index "@department:技術部 @age:[20 30]"Redis では
SAVEコマンドでデータベースのスナップショットを作成できる。BGSAVEを使用すると、バックグラウンドでスナップショットを作成する。注意点:
- Redis では一貫性制約はアプリケーション層で実装する必要がある
- インデックスは必要に応じて明示的に作成・維持する必要がある
- トランザクションは
MULTI/EXECを使用して実現する - メモリ使用量を考慮したキー設計が重要である
- バックアップ戦略は、アプリケーションの要件に応じて選択する
これらの操作は、アプリケーションのニーズに応じて組み合わせることで、効率的なデータ管理が可能となる。