はじめに
Redisでは、1つの処理が長時間に及ぶとブロックが発生します。
たとえば、大量の要素が登録されたセット型のキーを削除しようとしたような場合です。
ブロックが発生すると、クライアントが処理を実行しようとしても、ブロックが解消されるまでは応答が返ってこなくなります。
DEBUG SLEEPコマンドでブロックが発生したのと同じ状況を作り出すことはできるのですが、キーを削除していてブロックが発生するような状況を作り出したい場合には、ブロックが発生するようなデータを実際に作って確認したいものです。
環境に寄りますが、単純なキーの削除でブロックを発生させたい場合は、何千万個かの要素を登録したセット型のキーを用意して、そのキーをDELコマンドで削除するとブロックを発生させられます。
1つのキーに何千万個もの要素を登録するような設計を実際の運用で行うことはないと思いますが、ブロックを発生させての検証を行いたい場合には有効だと思います。
キーを作成するためのコード
下記の例では、セット型のキー1つに3000万個の要素を追加しています。
10~20個ぐらいの要素を追加するなら、単純にredis-cliでsaddコマンドを実行すればよいですが、3000万個ともなるとそれは無理ですので、Pythonでコードを書きました。
1回の処理で3000万個の要素を追加するのは無理だろうと思いつつ実施してみましたら、懸念したとおり、処理が強制終了しました。
よって、1回の要素の登録数を100万個にして、100万個の要素を30回追加するようにしましたところ、処理が可能なことを確認しました。
import redis
# 1回のsadd実行で登録する要素数
entry_num = 1_000_000
# saddを実行する回数
exec_sadd_times = 30
# キー名
key_name = 'mydata'
# Redisに接続する
r = redis.Redis(host='localhost', port=6379)
# セット型のキーに要素を登録する
# entry_num * exec-sadd_timesの数だけ要素を登録する
for e in range(0, exec_sadd_times):
# 要素の開始・終了の範囲を設定する
range_from = (entry_num * e) + 1
range_to = range_from + entry_num
# 要素を作成する
new_entries = list(range(range_from, range_to))
# セット型のキーを登録する
# 同じキーに対してsaddを実行するので、1つのキーに対して大量の要素を追加することになる
# saddに渡す値はアンパックする
r.sadd(key_name, *new_entries)
実行
シバンを付けたり、実行用パーミッションを付けるのは、各自で対応してください。
$ python set_mydata.py
キーの確認
キーが1つ登録され、当該キーの要素数が3000万個であることを確認しました。
この時のRedisのメモリ使用量は989.63MBでした。
127.0.0.1:6379> info keyspace
# Keyspace
db0:keys=1,expires=0,avg_ttl=0
127.0.0.1:6379> SCARD mydata
(integer) 30000000
127.0.0.1:6379> info memory
# Memory
used_memory:1037699024
used_memory_human:989.63M
used_memory_rss:1063030784
used_memory_rss_human:1013.79M
used_memory_peak:1078087568
used_memory_peak_human:1.00G
used_memory_peak_perc:96.25%
used_memory_overhead:870080
used_memory_startup:865968
used_memory_dataset:1036828944
used_memory_dataset_perc:100.00%
キーの削除を実行する(ブロックが発生する)
環境によりますが、先のキーの削除には数秒前後かかります。
この間はブロックが発生していて、他のクライアントからの処理を受け付けなくなっています。
たとえば、キーの削除中に他のクライアントからinfo
コマンドを実行すると、別のクライアントで実行しているキーの削除によるブロックが終わるまで、info
コマンドの結果は返ってきません。
2つのターミナルをそれぞれクライアントA・Bに見立てて、クライアントAでdel mydata
コマンドを実行した直後にクライアントBでinfo
コマンドを実行した結果は、次のとおりです。
127.0.0.1:6379> del mydata
(integer) 1
(7.71s)
127.0.0.1:6379> info ← クライアントAで del mydata を実行した直後に実行
# Server
redis_version:7.2.4
(略)
# Keyspace
(7.24s) ← クライアントAで「(7.71s)」が表示された後に応答が返ってきた。
本手順で作成したキーの使い道
del
コマンドの実行でブロックが発生することの検証の他に、以下の検証にも使えます。
-
unlink
コマンドでキーを削除した時の挙動- データをノンブロッキングで削除する
- lazyfree-lazy-user-delディレクティブを
yes
としてdel
コマンドを実行すると、unlink
コマンドと同じ処理を行う-
del
コマンドを使っても、強制的にunlink
コマンドを実行したときと同じ処理が行われ、ノンブロッキングで削除される
-