Redisでのbulk処理
Redisと仲良くなりたいです!って隣の人に言ったら、「障害に遭遇すると良いよ。それも深刻なやつにね。軽いやつだと軽い関係にしかなれないから」って言われたwyukawaです。こんばんは。
軽い関係じゃなくてもうちょっと踏み込んだ関係になりたいと思ってますが、まずは友達からお願いします > Redisさん
Redisでbulk処理やりたいってことありますよね。listにあるデータを1つづつlpopするんじゃなくてまとめてがつっとlpopしたいなんて状況です。
今回はrubyからアクセスする例ですので下記を使います。
https://github.com/redis/redis-rb
例えば10万件のデータを1つづつlpopするような以下のコードを試したところ僕の環境(Mac 10.7.5, rbenv+ruby 1.9.3p286, redis-rb 3.0.2, Redis 2.6.2)ではlpopに8秒ほどかかりました。コードにあるようにrpush時間は除いています。
require 'redis' r = Redis.new queue="nopipeline" count=100000 1.upto(count) {|i| r.rpush(queue,"#{i}") } start = Time.now result = [] 1.upto(count) { result << r.lpop(queue) } puts result.length puts "#{Time.now-start} seconds"
どうするのか調べてたら
http://redis.io/commands/lpop
のcommentにpythonコードのスニペットがあったのでこれを参考にrubyでも書いて試してみました。
こんな感じです。これを試すと1秒未満です。早いですね。
require 'redis' r = Redis.new queue="pipeline" count=100000 1.upto(count) {|i| r.rpush(queue,"#{i}") } start = Time.now size = count future = nil r.pipelined do r.multi do future = r.lrange(queue, 0, size-1) r.ltrim(queue, size, -1) end end result = future.value puts result.length puts "#{Time.now-start} seconds"
pipelineというのは言ってみればパラレルで処理するみたいなやつのようです。
本家のドキュメントはこちら
http://redis.io/topics/pipelining
日本語訳はこちら
http://redis.shibu.jp/developer/pipelining.html
multiはatominな処理を実現するやつみたいです。
r.pipelined do r.multi do future = r.lrange(queue, 0, size-1) r.ltrim(queue, size, -1) end end
とあるところのlrangeで0からsize-1分だけ取得します。
ltrimはlistからデータを削除するものです。
size番目から-1つまり最後までのデータを残して残りを削除します。
つまりlrangeで取得した0からsize-1分を削除することにります。
future.valueのところでデータを取得できます。
今回の例だと10万件のデータを1回で取ってくることになります。
phpでpipeline処理する場合は下記が参考になりそうです。
http://gihyo.jp/dev/feature/01/redis/0003
以上です。
2013/06/28 追記
pipelinedとmultiをセットで使うとmultiが効かないようです。redis-cli monitorでチェックしてもmultiが出てきませんでした。試した環境はMac 10.8.4, rbenv+ruby 1.9.3p327, redis-rb 3.0.4, Redis 2.6.10。
代わりに
r.multi do future = r.lrange(queue, 0, size-1) r.ltrim(queue, size, -1) end
のようにpipelinedを外して実行すると下記のようにmultiになりました。性能もpipelinedとmultiをセットで使っていたときと比べてそんなに変わりません。
$ redis-cli monitor ... 1372423549.654902 [0 127.0.0.1:50454] "multi" 1372423549.654989 [0 127.0.0.1:50454] "lrange" "pipeline" "0" "99999" 1372423549.718903 [0 127.0.0.1:50454] "ltrim" "pipeline" "100000" "-1" 1372423549.744641 [0 127.0.0.1:50454] "exec" ...
redis clientをマルチプロセスで実行する場合は要注意でしたね。。。