Redis の Lua Scripting におけるスクリプトキャッシュの管理
Redis には、サーバサイドで Lua スクリプトを実行する Lua Scripting 機能がある。積極的に使いたい機能というわけではないが、複数のコマンド呼び出しからなる Lua スクリプトを書いた場合に、スクリプト全体で atomic になるので、凝った Redis の使い方をする場合には役に立つ。
ただ、使い方に若干癖がある。シンプルに実行するだけなら EVAL
コマンドを使えばよいのだが、毎回スクリプトの文字列をパースすることになるので、パフォーマンスを考えるとよろしくない。
そこで登場するのが SCRIPT LOAD
と EVALSHA
である。SCRIPT LOAD
でスクリプトをロードしておいて、そのとき返ってくる SHA1 ハッシュ値を EVALSHA
コマンドに渡すことで、ロードしておいたスクリプトを EVAL
よりも高速に実行できる。
パフォーマンス的には改善されるものの、実際これをやるのは意外と厄介。SCRIPT LOAD
を呼び出して SHA1 ハッシュ値を控えておくというのはかなり面倒だ。ハッシュ値を管理するデータベースとか用意したくない。
というわけで、自分はこの方法は使っていない。会社の人がやっていてなるほどと思った方法があったので、それを使っている。この方法をさらに他の人に紹介したら感心されたので、ドキュメントやインターネット上の情報だけでこの方法にたどり着くのはそこまで簡単ではないのかなと思い、記事にしてみた次第。
その方法というのは、おもむろに EVALSHA
を実行するというもの。何もない状態で EVALSHA
に渡すハッシュ値をどうするのかと思うかもしれないが、実は単にスクリプト文字列の SHA1 ハッシュ値なので、手元で計算しても問題ない。つまり、先程述べたようなハッシュ値のデータベースを動的に構築する必要はない。
ただ、EVALSHA
だけだと、SCRIPT LOAD
されていないので当然失敗する。そこで、EVALSHA
に失敗した場合にはスクリプト文字列を渡して EVAL
する。EVAL
は実行したスクリプトをキャッシュする仕様になっているので、一度 EVAL
すれば SCRIPT LOAD
されたのと同じ状態になり、次回以降の EVALSHA
は成功するようになる。
この方法の利点は、起動時に SCRIPT LOAD
みたいな特別な処理が必要なく、単に EVALSHA
-> EVAL
とフォールバックするコードを書いておけばよいので楽ということ。初回以外は EVALSHA
だけで完結するので、性能上のペナルティもほぼない。
普通の Redis ではちょっと楽という程度だが、Redis Cluster を使う場合にはもう少し事情が変わってくる。というのも、Redis Cluster の場合、スクリプトのキャッシュは shard ごとに保持されている。SCRIPT LOAD
による方法だと、shard ごとにロードしておく必要があるし、途中で shard が増えたら増えた shard に対してロードする対応も必要になる。EVALSHA
-> EVAL
の方法であればこのあたりも意識する必要がない。各 shard について初回の EVALSHA
が失敗し、EVAL
が呼ばれるだけである。
管理方法というより管理しない方法になってしまった感もあるが、まあ管理しなくて済むなら管理しないのがよいだろう。