Redis Luaスクリプトでパフォーマンスを改善する (2)
以前の投稿では、Redisを利用して同時実行性の問題を解決する方法を紹介しました。
今回は、その解決策よりもパフォーマンス面でさらに優れた方法を紹介します。
Redisの公式ドキュメントによると、LuaスクリプトをRedisサーバー上で直接実行するメリットとして、いくつかのケースが紹介されています。
1. データ局所性
データが存在するRedisサーバー内で、ロジックを直接実行することを指します。2. アトミックな実行
一つのスクリプトが実行されている間、他のコマンドが割り込むことはありません。「All-or-Nothing」の原則で動作します。
これらの長所を活かして、以前実装したサーバーのパフォーマンスを改善していきます。
解決策
1. データ局所性
以前のコードでは、ロックを取得してからクーポンの在庫を減らすまでに、Redisサーバーへ合計4回のリクエストを送信していました。

Luaスクリプトを利用すれば、一度のリクエストで同じ操作が可能です。

2. アトミックな実行
一度のリクエストで送信されたスクリプトは、原子性(アトミック性)が保証されます。
では、どのようにして同時実行制御を実現しているかというと、複数のクライアントからスクリプトの実行リクエストがあった場合、Redisはリクエストが届いた順に一つずつ処理していきます。
下記は、この処理をJavaで実装したコードです。ここでのポイントは、redisTemplate.execute() を利用してRedisサーバーにスクリプトの実行を命令している点です。

前述の通り、Luaスクリプトはアトミックに実行されるため、処理の途中で失敗した場合は、実行前の状態へ完全にロールバックされます。
実際にRedisサーバーで実行されるLuaスクリプトは下記のとおりです。

このスクリプトによって、Redisサーバーから返される値とその意味は、例えば以下のように定義できます。
1を返す場合: クーポン発行成功
2を返す場合: 在庫切れによる失敗
3を返す場合: ユーザーが既にクーポンを保有しているため失敗
まとめ
この改善によって、同時実行性を保証しつつ、ネットワークコストを削減できました。
このクーポン問題以外にも、Luaスクリプトは様々な問題を解決できます。
例えば、Luaスクリプトは順次処理されるという特性を活かし、コンサートのチケット購入のように、先着順が重要な場面でリクエストを到着順に処理することができます。
データ局所性やアトミックな実行といった特性をうまく活用すれば、様々なシナリオに応用できるでしょう。
Discussion