Redis を理解する

zumikiti

December 21, 2023

どうも、先日マレーシアに行ったのですがお土産でドリアンチョコを買いすぎて途方にくれている zumi です。

今回はスターフェスティバル Advent Calendar 2023 の22日目として記事を書きました。

業務で Redis をよく使うのだが、手順にしたがってコマンドを叩いているだけで全然理解できていなかったので、改めてちゃんと理解しようと思った。

業務で使っているコマンドを理解する

redis-cli -h redis03.local -n 2 keys "*hoge*" | xargs redis-cli -h redis03.local -n 2 DEL

| 前のコマンドを実行してみる。

$ redis-cli keys "*hoge*"
1) "hoge-fuga"
2) "hoge"
3) "hoge-fuga-piyo"

つまり、keys でパターンマッチした結果を DEL の引数として渡すことで削除しているということ。

よし!完全に理解した!

さすがにこれだけでは何か言われそうなので、もう少し Redis について調べてみました。

redis とは何か

Redis is an open source (BSD licensed), in-memory data structure store used as a database, cache, message broker, and streaming engine. Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams. Redis has built-in replication, Lua scripting, LRU eviction, transactions, and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster.

You can run atomic operations on these types, like appending to a string; incrementing the value in a hash; pushing an element to a list; computing set intersection, union and difference; or getting the member with highest ranking in a sorted set.

To achieve top performance, Redis works with an in-memory dataset. Depending on your use case, Redis can persist your data either by periodically dumping the dataset to disk or by appending each command to a disk-based log. You can also disable persistence if you just need a feature-rich, networked, in-memory cache.

Redis supports asynchronous replication, with fast non-blocking synchronization and auto-reconnection with partial resynchronization on net split.

Redisはオープンソース(BSDライセンス)で、データベース、キャッシュ、メッセージブローカー、ストリーミングエンジンとして使用されるインメモリデータ構造ストアです。Redisは、文字列、ハッシュ、リスト、セット、範囲クエリ付きソートセット、ビットマップ、ハイパーログログ、地理空間インデックス、ストリームなどのデータ構造を提供します。Redisはレプリケーション、Luaスクリプト、LRUエビクション、トランザクション、さまざまなレベルのディスク上の永続性を内蔵しており、Redis Sentinelによる高可用性とRedis Clusterによる自動パーティショニングを提供します。

文字列への追加、ハッシュの値のインクリメント、リストへの要素のプッシュ、集合の交点、和集合、差集合の計算、ソートされた集合での最高順位のメンバーの取得など、これらの型に対してアトミックな操作を実行できます。

最高のパフォーマンスを達成するために、Redisはインメモリデータセットで動作します。使用するケースに応じて、Redisはデータセットを定期的にディスクにダンプするか、各コマンドをディスクベースのログに追加することでデータを永続化できます。機能が豊富でネットワーク接続されたインメモリキャッシュが必要なだけであれば、永続化を無効にすることもできます。

Redisは非同期レプリケーションをサポートしており、高速なノンブロッキング同期と、ネットスプリット時の部分的な再同期を伴う自動再接続が可能です。

引用元: https://redis.io/docs/about/


ちょっと小難しい単語が多いので、 ChatGPT に MySQL と PostgreSQL との違いも合わせて聞いてみた。

Redis(Remote Dictionary Server)は、オープンソースのデータ構造サーバーであり、メモリ内で動作し、キーと値のペアを格納することができます。主に高速なデータベース、キャッシュ、およびメッセージブローカーとして使用されます。以下に、Redisの特徴とMySQL、PostgreSQLとの主な違いをいくつか挙げてみましょう。

Redisの特徴:

  1. インメモリデータストア: Redisはデータをメモリ内に保存し、これにより非常に高速な読み取りおよび書き込み操作を提供します。

  2. データ構造: Redisは様々なデータ構造(文字列、ハッシュ、リスト、セット、ソート済みセットなど)をサポートしています。これにより、異なる用途に対応できます。

  3. パフォーマンス: Redisは非常に高いパフォーマンスを提供し、特に読み取り操作に強力です。これはメモリ内データストアの利点に起因しています。

  4. 単一スレッド: Redisは基本的に単一スレッドで動作します。これは、複雑なクエリやトランザクションの処理が行われる場合には効率が低下する可能性があることを意味します。

MySQLとPostgreSQLとの違い:

  1. データベースの種類: MySQLとPostgreSQLは、リレーショナルデータベース管理システム(RDBMS)であり、構造化されたデータを保存・管理します。一方で、Redisはキーと値のペアを保存するインメモリデータベースです。

  2. データモデル: MySQLとPostgreSQLは、テーブル、行、列を使用する伝統的なリレーショナルデータベースモデルを採用しています。一方、Redisはキーと値の組み合わせを使用し、より柔軟なデータモデルを提供します。

  3. トランザクション処理: MySQLとPostgreSQLはトランザクションのサポートがあり、ACID(Atomicity、Consistency、Isolation、Durability)の原則に基づいています。これに対して、Redisは単一スレッドの性質からくる一部の制約があり、トランザクション処理が相対的に単純です。

  4. パフォーマンス: Redisはメモリ内データベースであるため、読み取り操作において非常に高いパフォーマンスを発揮します。MySQLとPostgreSQLも高性能であり、トランザクション処理が必要な場合や複雑なクエリが必要な場合に利用されます。

簡潔に言えば、Redisは主に高速なデータアクセスが必要な場面や、キャッシュ、セッション管理などに適しています。MySQLやPostgreSQLは、より複雑なデータモデリングやトランザクション処理が必要な場合に選択されます。

ふむふむ、何となく毎度DB接続して取りに行くとパフォーマンスが低下する内容をキャッシュしておく場所程度にしか理解していなかったが、「なるほど〜そういうことか」という感じ。

redis の基本的な使い方

業務で、redis に直接値をセットしたりすることはないのですが基本的な使い方は知って損はないと思う。

環境は以下リポジトリの Docker 環境を使用している
https://github.com/zumikiti/webdev

redis-cli

redis-cli はコマンドを続けて書けば、そのまま結果を返してくれる。ただ、コマンドを書かなければ対話モードに入る。

$ redis-cli
127.0.0.1:6379> GET hoge
"hoge\\nhoge"
127.0.0.1:6379>

上記は、対話モードで hoge キーの中身を取得している。

死活監視 (PING)

文字通り、死活監視コマンド。レスポンスが PONG でピンポンになるのがオサレ。

$ redis-cli PING
PONG

文字列型のデータを扱う (SET, GET)

$ redis-cli SET hoge "hoge\nhoge"
OK
$ redis-cli keys "*hoge*"
1) "hoge"
127.0.0.1:6379> SET fuga "テストだよ"
OK
127.0.0.1:6379> GET fuga
"\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88\xe3\x81\xa0\xe3\x82\x88"

リスト型のデータを扱う (LPUSH, RPUSH, LRANGE)

$ redis-cli LPUSH mylist a b c 1 2 3
(integer) 6
$ redis-cli LRANGE mylist 0 -1
1) "3"
2) "2"
3) "1"
4) "c"
5) "b"
6) "a"

$ redis-cli LPUSH mylist x y z
(integer) 9
$ redis-cli LRANGE mylist 0 -1
1) "z"
2) "y"
3) "x"
4) "3"
5) "2"
6) "1"
7) "c"
8) "b"
9) "a"

$ redis-cli RPUSH mylist 7 8 9
(integer) 12
$ redis-cli LRANGE mylist 0 -1
 1) "z"
 2) "y"
 3) "x"
 4) "3"
 5) "2"
 6) "1"
 7) "c"
 8) "b"
 9) "a"
10) "7"
11) "8"
12) "9"

ハッシュ型のデータを扱う (HSET, HGET, HGETALL)

$ redis-cli HSET hash hoge "Hoge"
(integer) 1
$ redis-cli HGET hash hoge
"Hoge"

$ redis-cli HSET hash fuga "Fuga"
(integer) 1
$ redis-cli HGET hash fuga
"Fuga"
$ redis-cli HGETALL hash
1) "hoge"
2) "Hoge"
3) "fuga"
4) "Fuga"

$ redis-cli HSET hash test "test1" test2 "test2"
(integer) 2
$ redis-cli HGETALL hash
1) "hoge"
2) "Hoge"
3) "fuga"
4) "Fuga"
5) "test"
6) "test1"
7) "test2"
8) "test2"

キーを探す (KEYS, SCAN)

$ redis-cli KEYS "hoge"
1) "hoge"
$ redis-cli KEYS "*ge"
1) "hoge"
$ redis-cli KEYS "*g*"
1) "fuga"
2) "hoge-fuga"
3) "hoge"
4) "hoge-fuga-piyo"

SCAN は第一引数にカーソル(反復回数?)を指定する。0 の場合は、デフォルトの回数分反復実行される。
返却値の第一要素が残っている反復回数、第二要素にマッチしたキーが格納される。

この反復回数を制限することで、意図せずサーバーをロックしてしまうことを防げるとのこと。

Since these commands allow for incremental iteration, returning only a small number of elements per call, they can be used in production without the downside of commands like KEYS or SMEMBERS that may block the server for a long time (even several seconds) when called against big collections of keys or elements.

これらのコマンドはインクリメンタルな反復が可能であり、1回の呼び出しで返される要素の数が少ないため、KEYSやSMEMBERSのような、大きなキーや要素のコレクションに対して呼び出された場合に長時間(数秒でも)サーバーをブロックする可能性のあるコマンドの欠点なしに、実運用で使用することができます。

引用元: https://redis.io/commands/scan/

$ redis-cli SCAN 0 match "*g*"
1) "0"
2) 1) "fuga"
   2) "hoge"
   3) "hoge-fuga"
   4) "hoge-fuga-piyo"
$ redis-cli SCAN 0
1) "0"
2) 1) "fuga"
   2) "mylist"
   3) "hoge"
   4) "hash"
   5) "hoge-fuga"
   6) "hoge-fuga-piyo"
$ redis-cli SCAN 2
1) "0"
2) 1) "hoge"
   2) "hash"
   3) "hoge-fuga"
   4) "hoge-fuga-piyo"

なお、HSCAN などでハッシュ型のフィールドの検索などもできる。

$ redis-cli HSCAN hash 0
1) "0"
2) 1) "hoge"
   2) "Hoge"
   3) "fuga"
   4) "Fuga"
   5) "test"
   6) "test1"
   7) "test2"
   8) "test2"
$ redis-cli HSCAN hash 0 match "test?"
1) "0"
2) 1) "test2"
   2) "test2"
$ redis-cli HSCAN hash 0 match "test"
1) "0"
2) 1) "test"
   2) "test1"
$ redis-cli HSCAN hash 0 match "test*"
1) "0"
2) 1) "test"
   2) "test1"
   3) "test2"
   4) "test2"

終わりに

雰囲気で使っていることが多いので、こうやってちゃんと理解するのって大事だし楽しかった。
これで、来年から Redis ともっと仲良くなれる気がします。