Redis Hashes(パート1)

Hashはキーと値のコレクションで、オブジェクトの保存に最適です。このレッスンでは、基本的なハッシュ操作について解説します。

Hashとは

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

単一のRedisハッシュキーは複数のフィールドと値のペアを保存できます:

user:1
├─ name  → "Alice"
├─ age   → "25"
├─ city  → "Beijing"
└─ email → "alice@example.com"
💡 利点: Hashを使用すると、オブジェクト全体を読み取らずに個々のフィールドを変更できます。また、JSON文字列を保存するよりもメモリ効率が良いです。

HSETとHGET:フィールドの設定と取得

HSET:単一フィールドの設定

REDIS
# 単一フィールドの設定
HSET user:1 name "Alice"
(integer) 1  # 新しく追加されたフィールドの数を返す

# 複数フィールドの設定
HSET user:1 age 25 city "Beijing"
(integer) 2  # 2つの新しいフィールドを追加

# 既存フィールドの更新
HSET user:1 name "Bob"
(integer) 0  # フィールドは既に存在する、0を返す

HGET:単一フィールドの取得

REDIS
HGET user:1 name
"Alice"

HGET user:1 age
"25"

# フィールドが存在しない場合
HGET user:1 notexist
(nil)

HSETNX:フィールドが存在しない場合のみ設定

REDIS
# フィールドが存在しない場合のみ設定
HSETNX user:1 name "Charlie"
(integer) 0  # フィールドは既に存在、失敗

HSETNX user:1 phone "13800138000"
(integer) 1  # フィールドは存在しない、設定成功

HMSETとHMGET:バッチ操作

HMSET:バッチフィールド設定

REDIS
HMSET user:2 name "Bob" age 30 city "Shanghai" email "bob@example.com"
OK
⚠️ 補足: Redis 4.0.0以降、HMSETは非推奨です。複数フィールドを設定するにはHSETを使用してください。

HMGET:バッチフィールド取得

REDIS
HMGET user:2 name age city
1) "Bob"
2) "30"
3) "Shanghai"

# 存在しないフィールドを含む場合
HMGET user:2 name email phone
1) "Bob"
2) "bob@example.com"
3) (nil)  # phoneは存在しない

HGETALL:すべてのフィールドと値を取得

REDIS
HGETALL user:2
1) "name"
2) "Bob"
3) "age"
4) "30"
5) "city"
6) "Shanghai"
7) "email"
8) "bob@example.com"
⚠️ 補足: HGETALLはフィールド名と値の交互の配列を返します。ハッシュが大きい場合、大量のメモリとネットワーク帯域幅を使用します。注意して使用してください!

HKEYSとHVALS:すべてのフィールド名または値を取得

HKEYS:すべてのフィールド名を取得

REDIS
HKEYS user:2
1) "name"
2) "age"
3) "city"
4) "email"

HVALS:すべての値を取得

REDIS
HVALS user:2
1) "Bob"
2) "30"
3) "Shanghai"
4) "bob@example.com"

HEXISTS:フィールドの存在確認

REDIS
HEXISTS user:2 name
(integer) 1  # 存在する

HEXISTS user:2 phone
(integer) 0  # 存在しない

HDEL:フィールドの削除

REDIS
# 単一フィールドの削除
HDEL user:2 email
(integer) 1  # 1つのフィールドを削除

# 複数フィールドの削除
HDEL user:2 age city
(integer) 2  # 2つのフィールドを削除

# 存在しないフィールドの削除
HDEL user:2 notexist
(integer) 0

HLEN:フィールド数を取得

REDIS
HSET user:3 name "Charlie" age 28
HLEN user:3
(integer) 2  # 2つのフィールドがある

# キーが存在しない場合
HLEN notexist
(integer) 0

HSTRLEN:フィールド値の長さを取得

REDIS
HSET user:1 name "Alice" bio "Software Engineer"
HSTRLEN user:1 name
(integer) 5  # "Alice"の長さは5

HSTRLEN user:1 bio
(integer) 17  # "Software Engineer"の長さは17

Hashのユースケース

ユースケース1:ユーザー情報の保存

REDIS
# ユーザーの基本情報を保存
HSET user:1001 name "Alice" age 25 email "alice@example.com" role "admin"

# ユーザー名を取得
HGET user:1001 name

# ユーザー年齢を更新
HSET user:1001 age 26

# すべてのユーザー情報を取得
HGETALL user:1001

ユースケース2:ショッピングカート

REDIS
# ユーザー1のショッピングカート
HSET cart:user:1 product:101 2    # 商品101、数量2
HSET cart:user:1 product:102 1    # 商品102、数量1
HSET cart:user:1 product:103 5    # 商品103、数量5

# カート内のすべての商品を表示
HGETALL cart:user:1

# 商品数量を更新
HSET cart:user:1 product:101 3

# 商品を削除
HDEL cart:user:1 product:102

# カート内の商品数を確認
HLEN cart:user:1

ユースケース3:記事メタデータ

REDIS
# 記事情報を保存
HSET article:123 title "Redis Tutorial" author "Alice" views 1000 likes 50

# 閲覧数を増加(HINCRBYが必要)
HINCRBY article:123 views 1

# いいね数を増加
HINCRBY article:123 likes 1

# 記事のタイトルと著者を取得
HMGET article:123 title author

ユースケース4:商品キャッシュ

REDIS
# 商品詳細をキャッシュ
HSET product:2001 name "iPhone 15" price 5999 stock 100 category "phone"

# 商品価格を取得
HGET product:2001 price

# 在庫を更新
HSET product:2001 stock 95

# 商品が存在するか確認
HEXISTS product:2001 name

オブジェクト保存におけるHashとStringの比較

方法1:String + JSON

REDIS
SET user:1 '{"name":"Alice","age":25,"email":"alice@example.com"}'

# オブジェクト全体を取得
GET user:1

# 年齢を変更:読み取り、解析、変更、シリアル化、書き戻しが必要
# 複数ステップ、非効率

方法2:Hash

REDIS
HSET user:1 name "Alice" age 25 email "alice@example.com"

# 単一フィールドを取得
HGET user:1 name

# 年齢を変更:1ステップの操作
HSET user:1 age 26

# 複数フィールドを取得
HMGET user:1 name age

比較まとめ

項目 String(JSON) Hash
単一フィールドの変更 オブジェクト全体の読み取りが必要 直接変更可能
メモリ使用量 多い(JSON形式のオーバーヘッド) 少ない(コンパクトな保存)
単一フィールドのクエリ JSONの解析が必要 直接アクセス
複雑なネストオブジェクト ✅ サポート ❌ 非サポート
部分更新 ❌ 困難 ✅ 簡単
💡 選択ガイド:

  • シンプルなオブジェクト(フラット構造):Hashを使用
  • 複雑なオブジェクト(ネスト構造):String + JSONを使用
  • 個々のフィールドを頻繁に変更する場合:Hashを使用

Hashのエンコーディング最適化

Redisはハッシュのサイズに基づいてエンコーディングを自動的に選択します:

エンコーディング 条件 説明
ziplist フィールド <= 512、値の長さ <= 64バイト 圧縮リスト、メモリ節約
hashtable フィールド > 512 または 値の長さ > 64バイト ハッシュテーブル、パフォーマンス向上

エンコーディングタイプの確認

REDIS
HSET small:hash field1 "value1" field2 "value2"
OBJECT ENCODING small:hash
"ziplist"

# 多くのフィールドを追加した後
HSET large:hash field1 "value1" field2 "value2" ... field600 "value600"
OBJECT ENCODING large:hash
"hashtable"
ℹ️ 補足: エンコーディングはRedisによって自動的に選択されます。設定パラメータでしきい値を調整できます。

❓ よくある質問

Q Hashにはいくつのフィールドを保存できますか?
A 理論上は無制限(2^32 - 1)ですが、実際にはメモリによって制限されます。個々のハッシュは数千フィールド以下に抑えることを推奨します。
Q HGETALLはパフォーマンスに影響しますか?
A はい。ハッシュが大きい場合、HGETALLは大量のデータを返し、メモリと帯域幅を消費します。反復取得にはHSCANを使用してください。
Q Hashのフィールド値は数値にできますか?
A はい。Redisのすべての値は文字列ですが、数値文字列は算術演算(HINCRBY)をサポートします。
Q ハッシュキー全体を削除するにはどうすればよいですか?
A フィールドを削除するHDELではなく、キーを削除するDELを使用します:DEL user:1
Q Hashはネストをサポートしますか?
A いいえ。Hashの値は文字列のみです。ネストが必要な場合は、String + JSONを使用してください。

📖 まとめ

📝 練習問題

  1. ユーザー情報: Hashを使用してユーザー情報(名前、年齢、メール、都市)を保存し、すべての情報を取得しましょう
  2. ショッピングカート: Hashでショッピングカートを実装し、3つの商品を追加、1つの数量を変更、1つの商品を削除しましょう
  3. フィールド操作: HEXISTSでフィールドの存在を確認し、HLENでフィールドをカウントしましょう
  4. 比較練習: 同じオブジェクトをString + JSONとHashの両方で保存し、単一フィールドを変更する手順を比較しましょう

次のレッスン

次のレッスンでは、Redis Hashes(パート2)について学びます。高度なハッシュ操作と数値計算を解説します。

100%