Redisがシングルスレッドって知ってましたか?(わたしは知らなかったよ)

junichi_y
·

最近業務でRedis(PubSub)使っててこれからしばらくRedis触ることになりそうだったのでRedis本買いました。

いつもの読書メモ

Redisはシングルスレッドで動いている

まずこれを知らなかった。

ドキュメントのコマンド一覧に計算量載ってるのめずらしいなとおもってたらシングルスレッドで動作するRedisにとってコマンドの計算量というのはパフォーマンスにかなり影響するからなのね。

ほんとこれ知ってるか知らないかで実装のときの考え方変わるから知らなかった人は読んだ方がいいよ。

本番環境でKEYS使っちゃダメだよ、絶対

キー設計について

上述したようにRedisは導入するのも使うのも簡単だけどパフォーマンスのことを考えてちゃんと使うにはいろいろ考えて実装しないといけない。

ということを学んだ。

Mongo使ってたときによく見かけたけど安易になんでもHashデータみたいの作ると巨大ドキュメントみたいなのが出来上がるし、かなりのレコード数のListやSetを扱うことになるときに下手するとフルスキャンくらいになる可能性もある。

とはいってもString型じゃ単純なKey-Valueしか表現できないじゃんって思いますよね。わたしは思ってました

user:name:1, user:name:2みたいなkey設計でString型を扱えばクライアント側でこねこねすればある程度なんとかなるっぽい。

この発想はなかった。

なかったけど<userId>_nameみたいなkeyは無意識に使ってたや。

これをHashでuser_nameみたいなキーで{1: xxx, 2: xxx,....}みたいにすることもできるけどこれをした結果巨大ハッシュデータになると内部エンコーディングのメリットが活かしきれないようです。

考えなしにHashやListを使うのではなく、初手ではString型で実現できないかを考え、ListやHashが適切そうだと判断できたなら採用するくらいのほうがいいのかもしれない。

あと、実務でRedis ClusterのPubSubを実装していてCROSS SLOTエラーみたいのに遭遇した。(これgo-redisだとエラーにならんかったからかなりハマった)これはクラスターでマルチキー操作をする場合、同じSlotにあるkeyに対して操作しないといけないよというエラーらしい。

これは{user}:1, {user}:2みたいなキーにするとハッシュタグといって同じSlotに保存してくれるそうな。

こういうのあるから安易にクラスター構成選ぶべきではないと学んだ。

HyperLogLog

これ本書で初めて見た時印刷ミスかと思った。なんでLogLogなの?

PV数のようなカウントアップするだけでいい集計ならいいが、ユニークな値を集計しようとするとユニークな数だけメモリを食う。

そういうときにメモリ消費を抑えて集計だせるのがこのHyperLogLogらしい。ただ、誤差が1%程度生じるらしいのでざっくりした集計結果を出したい時に使えるっぽい。

意外と使う機会ありそうだから覚えておこう。

Redis Streams

PubSubするときに途中で参加したSubscriberが過去のデータを見れないということになるので、そのようなときはこのRedis Streamsを使うことになる。

シンプルなPubSubでもクラスターでやろうとすると全てのノードにメッセージをブロードキャストするというバグ?があるらしくせっかくクラスター構成にしても帯域圧迫するらしい。

なのでクラスター構成でPubSubしたい時にこのRedis Streamが使えたっぽい。

ただ、Redis7.0でShaded PubSubという機能が追加されてそのバグは解決されたっぽいけどクライアントに実装されているかは確認したほうがよさそう。

Redisの情報はありそうで少なく、特にクラスターやPubSub系の情報はかなり少ないし使う言語のクライアントライブラリの情報も必要。

あーでもないこーでもないと言いながら試行錯誤するはめになるのでなるべく枯れた機能を使った方がよさそうということを学んだ。

(絶賛Redis ClusterでShaded PubSubがLaravelで使えなかったり、testcontainers-goでクラスターPubSubがうまく動かなかったりしてる。)

まあ、クラスターか。。

ユースケースによると思うけどごりごりにRedis使う気ないなら単体構成のRedisにしといたほうが無難な気がする。レプリケーションで読み込みならスケールさせられるし。

とりあえず、無知で挑むクラスターは辛いぞ

ここら辺の話はLINE LIVEさんの記事が死ぬほど参考になりました、ありがとうございます。

Luaについて

これも知らなかった。

RedisではLuaで書いたスクリプトを実行できるらしい。古いのだとエフェメラルスクリプトで最近のだとRedisファンクション。

これも使える時がありそうなのでチャンスがあったら使っていこう

RTTの削減について

これも実務的な内容な気がしたので。

複数のコマンドを実行するとき、コマンドを実行するたびにRedisサーバーからの応答を待つ必要がある。

なのでMSETのようなマルチキー操作のようにまとめて送るのは有効。

ただし、データ型が異なる場合はMSETのようなコマンドは使えない。そのような場合はパイプラインを使うと良い。

パイプラインを使えば異なるデータ型もまとめてコマンドを送ることができる。ただし、処理がブロックされるわけではないので他のクライアントからの操作を受け付けてしまうためアトミック性みたいなものはない

一連の操作をしているときに処理をブロックしたいならばMULTI/EXEC(トランザクション)やLuaスクリプト、モジュールが使える。

トランザクションは簡単に実行できるが複雑なロジックは書けないので、複雑なロジックが必要な場合にLuaスクリプトの出番のようだ。

モジュールはRedis自体を拡張させるもので1番拡張性がありそうだがCで書くらしいので私には縁はなさそうだ

Redisクライアントの対応状況

もうすでに書いたけどRedisはさまざまな言語のクライアントがすでにあるようだけどもクライアントごとのRedisの機能のサポート具合はまちまち

特にクラスターとか比較的最近の機能

Redisクラスターは仕組み的にリダイレクトが肝で欲しいデータがリクエスト先のノードになかった時にどこのノードにあるかをリダイレクトメッセージで教えてくれる。

それを受け取るのはクライアントでリダイレクトするのもクライアント。そしてそのマッピング情報をキャッシュするのもクライアント。

クラスターに関してはクライアントの対応がかなり必要ということでいいのだろうか。

まったく対応してないことはないと思うのだけどいろいろ調べた上で決めると良さそう。

ちなみに、こういったクライアントの対応状況がまちまちなのも問題なのでRedis ProxyとかRedis Envoyみたいな名前のプロキシもあるんだか開発中だからしい

その他Redisで使われている技術たち

Redisの通信プロトコルはRESPというらしい。これについて深掘りする必要はないと思ってる(少なくとも機能実装するくらいなら)のだけど公式ドキュメントに急に出てきたりするとびっくりするので知っておくくらいはしたほうがいいかもしれない。

$echoe"TIME\r\nPING\r\nECHO\"test\"\r\n"|nclocalhost6379 *2 $10 1662383436 $6 228121 +PONG $4 test

こういうの

クラスターの設定をノード間で共有するのにRaftという合意形成アルゴリズムを参考にしているっぽいのとRedisRaftモジュールというものが開発中らしい。

Raftってプロックチェーンの合意形成アルゴリズムの話のときに出てきたような出てこなかったような。

こういう複数ノードからなる分散システムとかP2Pとかの話になると合意形成みたいなのが重要になってくるんだね。ていうか複数ノードでどう情報を共有するかとかなんかそこらへんの話は同じなのか。

ブロックチェーンって超すごい技術で成り立ってると思ってたけど昔からある分散システムとかの延長なんだろうな。知らんけど

おわりに

チューニングとかトラブルシューティング的なところはけっこう読み飛ばしてしまったけど思ってた以上に学びが多かった。

というかRedisを知らなすぎた。

AWSやGoogle CloudでもマネージドとしてRedisは使えるのでこれからRedisを使う場合はマネージドを採用することが多いだろう。

そうすると設定ファイルを直接チューニングしたりなどの運用業務的なものはほとんどなくなるんじゃないだろうか。

とはいえ監視的なものは必要そうだけど

でも、Redisのクラスターを複数のEC2で0から構成作るとかしなくていいんだもんな。いい時代でエンジニアやってるよ、ほんとに

いや、でも、ほんと得たもの多かったー

読んでよかったー

Redis使ってる人、もしくは使い人は読んだ方がいいよー

おすすめだよー

@junichi_y
エンジニアしてます。