Redis Hashes(パート2)

このレッスンでは、ハッシュフィールドの数値演算、反復クエリ、およびその他の高度な機能について解説します。

HINCRBY:フィールド値の増加

ハッシュフィールドは数値文字列を保存でき、アトミックな算術演算をサポートします。

HINCRBY:整数の増加

REDIS
# 記事統計を作成
HSET article:123 views 1000 likes 50 comments 20

# 閲覧数を増加
HINCRBY article:123 views 1
(integer) 1001

# いいね数を増加
HINCRBY article:123 likes 1
(integer) 51

# バッチ増加
HINCRBY article:123 views 10
(integer) 1011

# 減少(負の数を渡す)
HINCRBY article:123 likes -1
(integer) 50

HINCRBYFLOAT:浮動小数点の増加

REDIS
# 商品価格
HSET product:2001 price 99.99

# 価格を上げる
HINCRBYFLOAT product:2001 price 10.5
"110.49"

# 価格を下げる(負の数を渡す)
HINCRBYFLOAT product:2001 price -5.0
"105.49"

使用例:カウンターグループ

REDIS
# ユーザー統計
HSET user:stats:1 posts 0 followers 0 following 0

# 記事を公開
HINCRBY user:stats:1 posts 1

# フォロワーを獲得
HINCRBY user:stats:1 followers 1

# フォローする
HINCRBY user:stats:1 following 1

# 統計を表示
HGETALL user:stats:1
💡 利点: Hashを使用して複数のカウンターを保存すると、複数のStringキーを使用するよりもメモリ効率が良いです。

HSCAN:大きなハッシュの反復処理

ハッシュに多くのフィールドがある場合、HGETALLは一度にすべてのデータを返すため、サーバーをブロックする可能性があります。HSCANを使用すると、バッチ反復処理が可能です。

基本的な使用方法

REDIS
# 大きなハッシュを作成
HSET large:hash field1 "value1" field2 "value2" ... field1000 "value1000"

# 反復処理を開始
HSCAN large:hash 0
1) "127"          # 次の反復処理のカーソル
2) 1) "field1"
   2) "value1"
   3) "field2"
   4) "value2"
   ...

# 反復処理を続行
HSCAN large:hash 127
1) "0"            # カーソルが0の場合は反復処理完了
2) 1) "field999"
   2) "value999"
   ...

パターンマッチ

REDIS
# パターンに一致するフィールドのみを取得
HSCAN user:1 0 MATCH age*
1) "0"
2) 1) "age"
   2) "25"

反復処理ごとの件数を指定

REDIS
# 反復処理ごとに約100フィールドを返す
HSCAN large:hash 0 COUNT 100
💡 使用例: HSCANは、サーバーをブロックせずに大きなハッシュを反復処理するのに最適です。

高度なHashアプリケーション

ユースケース1:eコマース商品詳細

REDIS
# 完全な商品情報を保存
HSET product:3001 \
  name "iPhone 15 Pro" \
  brand "Apple" \
  price 8999 \
  stock 100 \
  category "phone" \
  sales 0 \
  rating 4.8

# 商品の基本情報を表示
HMGET product:3001 name price stock

# 販売数を増加
HINCRBY product:3001 sales 1

# 在庫を減少
HINCRBY product:3001 stock -1

# 評価を更新
HSET product:3001 rating 4.9

# 在庫を確認
HGET product:3001 stock

ユースケース2:ユーザー設定

REDIS
# ユーザー設定を保存
HSET user:config:1 \
  theme "dark" \
  language "en" \
  notifications "on" \
  privacy "public" \
  timezone "America/New_York"

# 単一の設定を取得
HGET user:config:1 theme

# 設定を変更
HSET user:config:1 theme "light"

# バッチで設定を取得
HMGET user:config:1 theme language notifications

ユースケース3:リアルタイム統計

REDIS
# 日次サイト統計
HSET stats:20260623 \
  page_views 0 \
  unique_visitors 0 \
  new_users 0 \
  orders 0 \
  revenue 0.0

# ページビューを増加
HINCRBY stats:20260623 page_views 1

# ユニークビジターを増加
HINCRBY stats:20260623 unique_visitors 1

# 注文数を増加
HINCRBY stats:20260623 orders 1

# 収益を増加
HINCRBYFLOAT stats:20260623 revenue 299.99

# 統計を表示
HGETALL stats:20260623

ユースケース4:A/Bテスト設定

REDIS
# 実験設定を保存
HSET experiment:homepage \
  button_color "blue" \
  layout "grid" \
  show_banner "yes" \
  discount_rate 0.1

# 実験設定を取得
HGET experiment:homepage button_color

# 実験設定を更新
HSET experiment:homepage button_color "red"

HashとJSONの比較

ストレージ比較

方法1:Hash

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

方法2:String + JSON

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

操作比較

操作 Hash String JSON
単一フィールド取得 HGET user:1 name JSONを解析する必要がある
単一フィールド変更 HSET user:1 age 26 読取→解析→変更→シリアル化→書き戻し
全フィールド取得 HGETALL user:1 GET user:1 + JSON解析
数値演算 HINCRBY user:1 age 1 解析→変更→シリアル化が必要
メモリ使用量 少ない 多い(JSON形式のオーバーヘッド)

ユースケース比較

シナリオ 推奨 理由
ユーザープロフィール Hash 独立したフィールド、頻繁な更新
商品情報 Hash 独立したフィールド、部分更新
複雑なネストオブジェクト String JSON Hashはネストをサポートしない
配列データ String JSON Hashは配列をサポートしない
設定オブジェクト Hash 独立したフィールド、高速クエリ
💡 選択原則:

  • 独立したフィールド、頻繁な部分更新 → Hash
  • 複雑なネスト、配列が必要 → String JSON
  • 数値演算が必要 → Hash

パフォーマンス最適化

1. 大きなハッシュを避ける

REDIS
# ❌ 推奨されない:単一のハッシュにフィールドが多すぎる
HSET big:hash field1 ... field10000

# ✅ 推奨:複数のハッシュに分割
HSET hash:part1 field1 ... field1000
HSET hash:part2 field1001 ... field2000

2. HGETALLの代わりにHSCANを使用

REDIS
# ❌ 大きなハッシュでHGETALLを使用
HGETALL large:hash  # ブロックする可能性がある

# ✅ HSCANで反復処理
HSCAN large:hash 0

3. バッチ操作でネットワークラウンドトリップを削減

REDIS
# ❌ 複数の個別操作
HSET user:1 name "Alice"
HSET user:1 age 25
HSET user:1 city "Beijing"

# ✅ バッチ操作
HSET user:1 name "Alice" age 25 city "Beijing"

4. エンコーディングしきい値の設定

redis.conf内:

CONF
# ハッシュフィールド数のしきい値(デフォルト512)
hash-max-ziplist-entries 512

# ハッシュフィールド値の最大長(デフォルト64バイト)
hash-max-ziplist-value 64
ℹ️ 補足: しきい値を上げるとメモリを節約できますが、パフォーマンスが低下する可能性があります。実際のデータに基づいて調整してください。

Hashの制限

1. ネストのサポートなし

REDIS
# ❌ ネストされたオブジェクトを直接保存できない
HSET user:1 address '{"city":"Beijing","zip":"100000"}'

# ✅ ネスト部分には文字列を使用
HSET user:1 city "Beijing" zip "100000"

2. 配列のサポートなし

REDIS
# ❌ 配列を保存できない
HSET user:1 tags ["redis","database","cache"]

# ✅ 配列にはセットまたはリストを使用
SADD user:1:tags "redis" "database" "cache"

3. 値は文字列のみ

REDIS
# すべての値は文字列
HSET user:1 age 25
HGET user:1 age
"25"  # 整数ではなく文字列を返す

❓ よくある質問

Q HINCRBYを数値以外のフィールドで使用するとどうなりますか?
A エラーが返ります:(error) ERR hash value is not an integer。フィールド値が数値文字列であることを確認してください。
Q Hashのフィールドは順序付けられていますか?
A いいえ。Hashのフィールドは順序なしです。HGETALLは特定の順序を保証しません。
Q キーがHash型かどうかを確認するにはどうすればよいですか?
A TYPEコマンドを使用します:TYPE user:1hash を返します。
Q HSCANは重複がないことを保証しますか?
A いいえ。HSCANは重複フィールドを返す可能性があります。アプリケーション層で重複排除を処理する必要があります。
Q HashとSetの違いは何ですか?
A Hashはフィールドと値のペアを保存し、Setは一意な要素を保存します。Hashはオブジェクト用、Setはタグや関係用です。

📖 まとめ

📝 練習問題

  1. 数値演算: Hashを使用して記事統計(閲覧数、いいね数、コメント数)を保存し、HINCRBYで増加させましょう
  2. 商品管理: Hashで商品情報を保存し、表示、変更、販売数の増加、在庫の減少を練習しましょう
  3. 設定管理: Hashでユーザー設定を保存し、単一設定の取得、バッチ取得、設定の変更を練習しましょう
  4. パフォーマンス比較: HashとString JSONで単一フィールドを変更する手順を比較しましょう

次のレッスン

次のレッスンでは、Redis Lists(パート1)について学びます。基本的なリスト操作を解説します。

100%