概要
redisでsetコマンドのnxオプションというものがあり setする際に、キーがまだ存在していない場合にのみ値をセットできるもので、redisによるロック処理に使われます。 このnxオプションについて調べてみます。
NX -- Only set the key if it does not already exist.
setコマンドでのnxオプションをつけてみる
redis-cli を実行し、redisに対して set とNXオプションとEXによるTTLを設定し 簡単なロック機能を試してみます。
set key value NX EX 10
というコマンドを実行します。
- NX: コマンド実行時に既にキーが存在していない場合のみsetできる。 そうでない場合はnil
- EX 10: 10秒間のTTLを設定する。 キーを登録してから10秒を超えるとキーに紐づくデータが消える
上記のコマンドにより、10秒間のロック機能を実現します。
redis-cli 127.0.0.1:6379> set key value NX EX 10 OK # ここでロックがかかる 127.0.0.1:6379> set key value NX EX 10 (nil) 127.0.0.1:6379> set key value NX EX 10 (nil) 127.0.0.1:6379> set key value NX EX 10 (nil) 127.0.0.1:6379> set key value NX EX 10 (nil) 127.0.0.1:6379> set key value NX EX 10 (nil) ------ ここまでロックをかけてから10秒間の間は set nxをしてもnilとなる 127.0.0.1:6379> set key value NX EX 10 OK # 10秒経ったので新たにロックをかけることができる
これにより、キーがない状態で set key value NX EX 10
を実行してロックをかけ、その後10秒間は
同じコマンドを実行してロックを獲得しようと試みても失敗することを確認できました。
redisがシングルインスタンスの場合、このset でのNXとEXオプションをつけることで簡単なロック機構を実現できます。
set nxオプションが付いていた場合の内部実装について
nxオプションをつけた時にsetコマンドではどういう処理になっているかを説明します。
redis本体のソースコードでの実装箇所は、 src/t_string.cのsetGenericCommand
関数から辿ることができます。
redis/src/t_string.c at 7.2.5 · redis/redis · GitHub
set実行時のNXオプションがついているかの判定
set実行時のオプション引数のパース処理をみてみます。
パース処理はparseExtendedStringArgumentsOrReply
関数で実装されています。
redisに対するコマンドの引数をパースして、 NXオプションが設定されていたら SET NX用のフラグ OBJ_SET_NX
を立てる処理を行っています。
... if ((opt[0] == 'n' || opt[0] == 'N') && (opt[1] == 'x' || opt[1] == 'X') && opt[2] == '\0' && !(*flags & OBJ_SET_XX) && (command_type == COMMAND_SET)) { *flags |= OBJ_SET_NX; } ...
redis/src/t_string.c at f60370ce28b946c1146dcea77c9c399d39601aaa · redis/redis · GitHub
OBJ_SET_NXフラグがたっている場合の setコマンドの実行
続いて、NX用のフラグが立っている場合にsetコマンドでどういう処理が行われるかをみてみます。
setコマンドの処理はsetGenericCommand
関数で実装されています。
関数の前半の方で、まず指定したキーがデータに存在しているかを確認し、存在していた場合はアーリーリターンして、コマンドの実行を終了し、その後にデータを登録する処理はスキップされます。 これにより、既にキーが存在していた場合 setでnxオプションをつけて実行された場合はデータの登録はされずにスキップされることになります。
// set予定のキーが登録済みか確認する found = (lookupKeyWrite(c->db,key) != NULL); // NXフラグまたはXXフラグが設定されていて、キーが見つかった場合 if ((flags & OBJ_SET_NX && found) || (flags & OBJ_SET_XX && !found)) { if (!(flags & OBJ_SET_GET)) { addReply(c, abort_reply ? abort_reply : shared.null[c->resp]); } return; } // この後、setの処理が続く ...
以上となります。
EXまたはPXオプションを設定した場合、指定した時間が経ったらキーの値が削除され、ロックが解除されることになります。
その後再度ロックをかけることができます。