🐡

初心で挑むredis入門 ~Redis hashes編~

に公開

今回はredisで使えるhashesについてみていきます。昨日公開したStringsについてもぜひご覧ください。

https://zenn.dev/akasan/articles/redis_datatypes

早速検証!!

redisの環境構築については先日公開した以下の記事を参考にしてください。

https://zenn.dev/akasan/articles/redis_quickstart

Redis hashesのドキュメントは以下になります。

https://redis.io/docs/latest/develop/data-types/hashes/

hashesについて

Redis hashesはfield-valueペアを登録するための構造化データ格納のためのデータ型です。Pythonでいうところの辞書型(ただしネストがないもの)を格納するものと思ってください。

データの格納

hashesにデータを登録するにはhsetコマンドを使います。

data_hashes_set.py
import redis

r = redis.Redis(host="localhost", port=6379, db=0)
r.hset(
    "hashes_sample:1",
    mapping={
        "key1": "value1",
        "key2": "value2",
        "key3": "value3",
    },
)

r.hsetの引数として、一番目にデータを登録するhashの名前を、mappingに具体的なfield-valueペアを登録します。こちらのコードを実行するとredisにhashed_sample:1という名前でデータが登録されます。

uv run data_hashes_set.py

データの取得

それでは先ほど設定したデータを取得するコードを作成してみましょう。

data_hashes_get.py
import redis

r = redis.Redis(host="localhost", port=6379, db=0)
print(f'{r.hget("hashes_sample:1", "key1")=}')
print(f'{r.hgetall("hashes_sample:1")=}')
print(f'{r.hmget("hashes_sample:1", ["key1", "key3"])=}')

データを取得する場合は、以下の関数を利用できます。

  • hget: 指定したhashについて、単徳のvalueを取得できる
  • hgetall: 指定したhashについて、すべてのfield-valueペアを取得できる
  • hmget: 指定したhashについて、複数のfieldに対応するvalueを取得できる

上記の例では、key1の値たけ、すべての値のペアを、key1key3の値をまとめて取得する3つについて実行しています。
それでは早速コードを実行してみましょう。

uv run data_hashes_get.py

# 結果
r.hget("hashes_sample:1", "key1")=b'value1'
r.hgetall("hashes_sample:1")={b'key1': b'value1', b'key2': b'value2', b'key3': b'value3'}
r.hmget("hashes_sample:1", ["key1", "key3"])=[b'value1', b'value3']

結果を見ると、想定していた挙動になっています。なお、hgetallはfield-valueペアとして取得するので、値だけでなくキーも取得されます。

カウンターとしての利用

昨日利用したRedis Stringsと同様、hashesでもカウンターとして利用できます。

https://zenn.dev/akasan/articles/redis_datatypes#カウンターとして利用する

hincrbyを利用することで、指定したhashの指定したfieldに対応するvalueを指定した値だけ増やすことができます。

data_hash_count.py
import redis

r = redis.Redis(host="localhost", port=6379, db=0)

r.hset(
    "hashes_sample:2",
    mapping={
	"count": "0",
    },
)

print(f'{r.hget("hashes_sample:2", "count")=}')
r.hincrby("hashes_sample:2", "count", 1)
print(f'{r.hget("hashes_sample:2", "count")=}')
r.hincrby("hashes_sample:2", "count", 100)
print(f'{r.hget("hashes_sample:2", "count")=}')
r.hincrby("hashes_sample:2", "count", -33)
print(f'{r.hget("hashes_sample:2", "count")=}')

上記のように、カウンタ対象のデータをint型にパースできる値に設定しておけばカウンターとして利用できます。r.hcinrbyの第一引数にhash名、第二引数にfield名、第三引数に増やしたい値を指定すると、指定した値の分だけ変化させることができます。

早速コードを実行してみると以下の結果となりました。指定した値をカウンターとして利用できていることが確認できました。

uv run data_hash_count.py

# 結果
r.hget("hashes_sample:2", "count")=b'0'
r.hget("hashes_sample:2", "count")=b'1'
r.hget("hashes_sample:2", "count")=b'101'
r.hget("hashes_sample:2", "count")=b'68'

データの期限の設定

hashesではデータに対して期限を設けることができます。例えばあるhashのXというフィールドは指定されてから100秒だけ維持されるが、100秒を経過したら削除するのような使い方ができます。

まずは例を見てみましょう。最初の例と同様のhashを作成し、key1key3が3秒ごに削除されるようにしてみます。

data_hash_expire.py
import redis
import time

r = redis.Redis(host="localhost", port=6379, db=0)

r.hset(
    "hashes_sample:3",
    mapping={
        "key1": "value1",
        "key2": "value2",
        "key3": "value3",
    },
)
r.hexpire(
    "hashes_sample:3",
    3,
    "key1",
    "key3",
)

print(f'{r.hgetall("hashes_sample:3")=}')
time.sleep(4)
print(f'{r.hgetall("hashes_sample:3")=}')

今回重要なのはhexpire関数です。第一引数にはhash名を、第二引数にはデータを維持する期間を、第三引数以降で対象となるfield名を指定します。今回の例で言うと、hashes_sample:3key1key3が3秒後に削除されるということになります。

r.hexpire(
    "hashes_sample:3",
    3,
    "key1",
    "key3",
)

それでは早速コードを実行してみましょう。

uv run data_hash_expire.py

# 結果
r.hgetall("hashes_sample:3")={b'key3': b'value3', b'key1': b'value1', b'key2': b'value2'}
r.hgetall("hashes_sample:3")={b'key2': b'value2'}

結果を見ると、hexpire設定前はすべての値が揃っていますが、指定時間を経過してから再度アクセスすると指定したfield(key1key3)が削除されていることが確認できました。

なお、期限の設定についてはこのほか多数機能が用意されており、以下のような機能が用意されています。

  • hexpire: 指定したfieldに対して指定した時間(秒)だけ維持する
  • hpexpire: 指定したfieldに対して指定した時間(ミリ秒)だけ維持する
  • hexpireat: 指定したfieldに対して指定したUNIX epochの秒まで維持する
  • hpexpireat: 指定したfieldに対して指定したUNIX epochのミリ秒まで維持する
  • hexpiretime: 指定したUNIX epochの期限を秒単位で取得する
  • hpexpiretime: 指定したUNIX epochの期限をミリ秒単位で取得する
  • httl: 指定したfieldに対して残りの維持時間を秒単位で取得する
  • hpttl: 指定したfieldに対して残りの維持時間をミリ秒単位で取得する
  • hpersist: 指定したfieldに対して期限を解除する

まとめ

今回はRedis hashesについて調べてみました。ハッシュマップを利用できるだけでなく、データに対して期限を設けることができると言うことで、キャッシュ的な用途で使えそうだと思いました。ネストデータを取り扱うことはできないので、その場合はStringsでネストされたデータをエンコードして保存したりが必要そうですね(そもそもネストデータが本当に適切かは検討した方が良さそうですが)。次回はListsを取り扱おうと思います。

お知らせ

初心で挑むredis入門をスクラップにしてまとめていこうと思いますので、ぜひご覧ください。

https://zenn.dev/akasan/scraps/1c9848be3a2bb0

Discussion