実践Redis入門(技術評論社)
https://image.gihyo.co.jp/assets/images/ogp/2022/9784297131425.jpg#.png
Redisは高速な応答性などからRDBMSでは対応しきれないデータの格納などに用いられます。高速さと柔軟さを併せ持ち,数多くのプログラミング言語から利用できます。
RDBMSの前段に配置してキャッシュとして用いるときの圧倒的な応答性能,Redisの多様なモデルによるアプリケーションからの扱いやすさなどで人気を集めています。リアルタイムの処理や高速なサービスが求められるWebサービスで特に支持を得ています。
本書では,このRedisを基礎から実践まで解説します。体系的な知識が身につき,アプリケーション開発や運用,トラブルシューティングがこなせるようになります。
avashe.iconredisへの直感を養うことで、kvs全般への理解を深めたい シングルスレッドで処理されるが徐々に最適化する部分はオプションによってマルチスレッドになっている
アクセスの処理はシングルで、IOや削除の処理がマルチスレッド
pp.8にredisはアトミックではあるがロールバックを持たないという説明がある
ちょっとよくわからない、後ろの章で説明されるのでそれを待つ
TCPの上に実装されている
redis-benckmarkがベンチマークツールとして用意されているが、RESPに対応したベンチマークツールがあれば他のRESP互換DBともっと比較しやすいのでは?
緯度、経度、地点の名前の組みのこと
Luaが入っており、サーバ側でifなどのロジックを走らせながらまとめて処理したいときに便利
ストアドプロシージャの代わりみたいな
redisを使う時の心構え(ミニベストプラクティス)
ACIDをサポートしてないため、原則として、なるべく複数のキーの間に依存関係を作らないこと
ただし、Redisでも特定の条件下で複数キーの処理をする機能があるので、例外はある
特定のキーを触ると、事前に指定されたキーを処理しに行く、mysqlで言うところのtriggerみたいな機能が提案されているらしい(キーアノテーション機能)
依存関係がある値は、1つのキーで参照する値の中にまとめること
型を多く用意してニーズに応じた最適化がなされているので、なんでもかんでもStringにせず、使い分けを意識すること
redisはキーの命名規則でスキーマを表現する
user:<ユーザー番号>:followersのようにする
大ジャンルごとにコロン(:)などの文字列で区切って、個別のデータを持たせることが多い
ただし、Hash型などでまとめることも検討すること
他にもNoSQLの設計技法が流用できる
複数の値をまとめて処理できる命令を使うと、RTTのレイテンシーを短縮できる
他にも命令をためてバッチで発行するパイプライン機能が利用できる、これは後述
計算量が大きい処理をするとその間、外からはハングアップしているように見えるのでなるべくO(N)の処理をしないこと
アクセスがシングルスレッドのため
特にキーを一括で取得する系のKEY命令は、大きいkvsで実行すると大変なことになってしまうので非推奨
代わりに*SCAN系のコマンドを使うとよい。これは指定した数だけバッチで読み込んでから返してくるイテレータ(cursor based iterator)である。例えばこれで10件ずつ読み進め、必要なものを見つけ次第処理を終えるように実装すれば、長大なkvsを全部読むことにならずに済む。 基本的には型ごとに命令があり、GET命令と、SET命令が基本である
そして型ごとに特殊な命令がある
命令の名前は大文字小文字を無視するが、キーは大文字小文字を区別する
String型
構造を持たない1:1対応のk → vがString型
プログラミング言語ではこれをStringを返すハッシュテーブル型と言うだろうが、redisはkvsなのでString型といったらk → vという構造も含まれている
Redisではプログラミング言語におけるいわゆる文字列、バイナリーデータ、整数、浮動小数点数いずれもString型(文字列)
ただし文字列が数値(integer)の場合限定の命令もある(数値の増減ができる)
RedisのStringのサイズはかつて512MBだったが、現在でははproto-max-bulk-lenディレクティブで設定可能
client-query-buffer-limitはそれより大きくすること
proto-max-bulk-lenが10Gなら、client-query-buffer-limitは11G
これは1クライアントからのクエリーバッファーの最大サイズであり、RESPのフォーマットを含めたバイト数で制限される
webアプリのキャッシュとしては、セッションを指すIDをkとして諸々のJSONをvとするString型が典型的
TTLを設定できる命令が多く、キャッシュの失効なども表現できる
List型
k → v でListという名前の双方向キューへの参照が得られる型
双方向キューと想像してできるようなことは一通り用意されている
両端はO(1)でアクセスできるが、中央にアクセスしようとすると計算量がかかる
スタックやキューの実装から、ログやSNSのタイムライン、webサイトの人気投稿10件みたいなランキングの実装などに使える
このために、{L, R}RANGEという範囲を指定してまとめて取得する操作がある
Hash型
k → vで更にハッシュテーブルへの参照が得られる型
1つのハッシュに保存できるフィールド数は2^32 - 1(約43億)
メモリー上に余裕があれば事実上制限はない
ただし後述の理由で大きくすることはデメリットがある
例えば以下のようにキーの設計で1つのユーザの属性を表現しようとする場合、Hash型で束ねることを検討しよう
code:redis
SET user:1:name "Suzuki Taro"
SET user:1:age 30
Hash型は小さい場合に特殊な形式に内部で変換され、よりメモリ消費量が小さくなるのでStringを複数作るより有利になる
注意点
Hash型の中身に個別でTTLを設定できないという制約があるため注意
要素数もしくは要素のサイズが大きすぎると以下の注意点が生じるため、複数の小さいHash型かString型に分割すべき
ハッシュテーブルを効率のいい形式に最適化してくれなくなる
最適化パラメータをいじることでも軽減は可能
Redisクラスターを構築する際にシャーディングしづらくなる
HDELコマンドが遅くなる
HDELはハッシュテーブルの指定した要素を削除するコマンドで、ハッシュテーブルの要素数に対しO(N)かかる
実装によっては規模が大きくなることでHDELが数秒かかることもあるので、ベンチマークをとること
Set型
k → vでString型の集合が得られる型
用途
あるリソースに対して付与している1つ以上のタグ情報を管理する場合
日時ごとの訪問者を管理する集合に対して月単位のユニークな訪問者を算出するといった集合間の演算をする必要がある場合
莫大な要素数をカウントする必要がある場合、誤差を許容するならHyperLogLog機能があるのでそっちを使おう メモリ使用量は要素数に比例して大きくなる
SortedSet型
順序が付いたSet型
Set型の要素にスコアと呼ばれる浮動小数点数が割り振られていて順番で取り出すことができる
要素は常にスコアでソートされている
スコアは値を自由に設定できる
スコアの範囲で取り出すことができる
用途
ランキング
アクティビティフィード
ランキングを容易かつシンプルに実現できる
SortedSet型で同一スコアを持つ要素が複数ある場合、キーの辞書順でソートしてしまう。これはユーザ名でランキングの有利不利が出てしまうので良くない。ここで例えばランキングに乗る時刻順で優劣をつけたい場合、スコアをビット演算で左に数桁シフトし、その空いた桁にタイムスタンプを付けるとよい。他にはキーにプレフィックスやサフィックスを付け、それを元にアプリ側で表示情報をハンドリングするなどが考えられる。
EXPIREがあるから結構雑にタイムスタンプ付きのkeyを作って放り込んじゃっていいのがredis
bitmap
内部的にはString型、バイナリ列を保存、ビット演算できる
地理空間インデックス
内部的にはSortedSet型
ある地点の周囲一定距離の要素を取り出したりできる
Pub/Sub
チャンネルとPub/Subというロールを用意して、そのチャンネルに配信されたデータが購読者全員から読めるというシステムがRedisだと簡単に用意できる
素朴なチャットサービスはまさにこれ
注意点
Subscriberは自分が購読した地点より前のデータを読むことができない
一時的にクライアントがアクセス断している間にPublishされたデータを読むことができない
これを実装したい場合はListに別途保存するなどの実装が必要
Subscriberでメッセージを消費できないクライアントがあると、Redis側の負荷が増大するので、一定以上遅延したら切断するみたいな閾値が設定できる
Redis 7.0以降では、Sharded Pub/Sub機能が導入された
Redis clusterを使っている際に普通のPub/Subを使うと、クラスタの全ノードに書き込みが共有されるので、スループットが高いPub/Subがあるとクラスタの負荷が高まってしまう
Sharded Pub/Subを使うと、購読の範囲がシャード内のノードに制限される
メリット
数え上げる対象の数に比例するメモリーを必要とせず、一定量のメモリーのみ
最大でも約 12kバイトのメモリー量。要素数が非常に少ない場合はより小さい
デメリット
標準誤差 1%未満の計算誤差あり
ユースケース
ユニークな訪問者数の計算など
ここまで紹介したRedisのデータ型は色々な条件によって内部エンコーディングが変わる
(pp.95 Column 内部エンコーディング)
つまりメモリやCPUの最適化時に、理解が必要になってくる
他にも自前でのエンコーディングがメモリ最適化に効いてくる可能性もある