📌

Redis、Lua Script vs Transaction

2020/11/16に公開

Redis の Transaction は、RDBMS脳には難しい

Redis の Transaction で行われている処理は、キューイング後の Atmic な一括処理だけです。
コマンド途中にエラーが発生してもロールバックは行われません。

Redis Transaction の例

よくある利用方法としては、 INCR + EXPIRE を Atomic に実行したいときでしょうか。
例えば、下記のようなコマンドになります。

redis> MULTI  # ここで Transaction 開始
OK
> INCR hoge
QUEUED
> EXPIRE hoge 60
QUEUED
> EXEC # ここでキューイングされたコマンドが Atomic に実行
1) (integer) 1
2) OK

INCR のような value に数値が期待されているコマンドを、 value が文字列の key に対して発行すると当然エラーが発生します。

redis> SET hoge fuga
OK
redis> GET hoge
"fuga"
redis> INCR hoge
ERR ERR value is not an integer or out of range

上記を Transaction で実行すると、ロールバックせずに SET hoge fuga はそのまま残る。

redis> MULTI  # ここで Transaction 開始
OK
redis> SET hoge fuga
QUEUED
redis> GET hoge
QUEUED
redis> INCR hoge
QUEUED
> EXEC # ここでキューイングされたコマンドが Atomic に実行
1) OK
2) "fuga"
3) (error) ERR value is not an integer or out of range

なぜロールバックされないのか?

Transactions に記載されている思想設計が理由です。

  1. エラーの原因はプログラミング中にあり、これは本番環境に入るまでの開発途中で気づくことが出来るため
    • Syntax Error や Wrong Data Type に起因するため
  2. Redis は内部的にロールバックを実装していないから、シンプルに高速になっているため

つまり Redis の Transaction とはなにか?

複数のコマンド実行時に、他のクライアントに割り込まれないように Atomic に実行させる機構です。

Redis のもうひとつの Atomic な実行方法

Redis には Lua Scripting という、もうひとつの Atomic な複数コマンド実行方法があります。

例えば一番最初の INCR + EXPIRE を Lua Script で実装すると下記のようになります。

redis> EVAL "local r = redis.call('INCR', KEYS[1]) redis.call('EXPIRE', KEYS[1], ARGV[1]) return r" 1 hoge 60
(integer) 1

Lua Script vs Transaction

Redis scripting and transactions に記載の通り、 Lua Script が v2.6 で実装されたのに対し、 Transaction は v1.2 で実装されました。
そして、通常 Lua Script の方が単純で高速な上、中間値も扱えるなど出来ることも多いです。

そのため Redis 公式には、 Lua Script が広く使われるようになれば Transaction 機能を廃止する可能性もあると記載されています。

一方で、 Lua Script は逆になんでも出来てしまう点で非常に注意が必要です。
デバッガとして Redis Lua scripts debugger も存在はしていますが、それでもロジックがアプリケーション外に散るデメリットもあります。

そのため、よく使うコマンドの組み合わせ (INCR+EXPIRE など ) のみを予め実装した Helper や Utility クラスを用意しておくのもひとつの手です。

一方で Transaction は工夫しなくても、コードが意味的に読みやすいため、実装ミスによる Redis を長時間ブロッキングしてしまうリスクも小さいです。

Lua Scripting の注意点を共有しながら最大限効果を享受するか、わかりやすさと機能制限を重視して Transaction を利用するか
開発チーム内で十分話し合って、方針を決めると良いでしょう。

Discussion