こんにちは
村上です。
ちょっと高速に読み書きできる共有ストレージが案件で必要になったので、KVSのmemcachedとredisを触ってみました。
インストール
どちらもyumでインストールできると思います。
1 2 |
sudo yum install memcached sudo yum install redis |
インストールが終わったら起動です。
1 2 |
sudo /etc/init.d/memcached start sudo /etc/init.d/redis start |
今回はどちらもRubyから操作します。
1 2 |
gem install dalli gem install redis |
ってして、もしくはGemfileに書いて「bundle install」 準備はOK
ちなみに「dalli」はmemcached用のgemです。
(読み方は「ダリ? ダルリ!?」)
memcached
まず、memcachedのdalliですが、こんな感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
require 'dalli' dalli = Dalli::Client.new('localhost') dalli.set(:string, "hoge") # 文字列hogeをセット dalli.get(:string) # 文字列を取得 dalli.set(:time, Time.now) # Timeをセット dalli.get(:time) # Timeを取得。Timeオブジェクトで取得可能 dalli.set(:hash, {:a => "a", :b => "b"}) # ハッシュをセット dalli.get(:hash) # ハッシュを取得 ハッシュで取得可能 dalli.set(:array, ["1", "2", "3"]) # リストをセット dalli.get(:array) # リストを取得 リストで取得可能 dalli.set(:string, "hoge", 5) # 第3引数に整数をセット dalli.get(:string) # 5秒間はデータ取り出し可能。5秒後にデータは削除される |
redis
続いてredisですが、
1 2 3 4 5 6 7 8 9 |
require 'redis' redis = Redis.new redis.set(:string, "hoge") # 文字列hogeをセット redis.get(:string) # 文字列を取得 redis.set(:time, Time.now) # Timeをセット redis.get(:time) # Timeを取得が文字列で取得される!! |
ってことで、redisの場合は、オブジェクトは文字列となって取得されちゃいます。
なんで、下記のようなコンバートを実施
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
require 'redis' def encode(val) Marshal.dump val end def decode(val) Marshal.load val end redis.set(:string, encode("hoge")) # 文字列hogeをセット decode(redis.get(:string)) # 文字列を取得 redis.set(:time, encode(Time.now)) # Timeをセット decode(redis.get(:time)) # Timeを取得。Timeオブジェクトで取得可能 redis.set(:hash, encode({:a => "a", :b => "b"})) # ハッシュをセット decode(redis.get(:hash)) # ハッシュを取得 ハッシュで取得可能 redis.set(:array, encode(["1", "2", "3"])) # リストをセット decode(redis.get(:array)) # リストを取得 リストで取得可能 |
こんな感じでいけそうです。
あと、redisの場合は
1 2 |
redis.setex(:string, 5, encode("hoge")) # 第2引数に整数をセット decode(redis.get(:string)) # 5秒間はデータ取り出し可能。5秒後にデータは削除される |
にする必要があります。メソッドが変わって、dalliとは引数の場所が違うことに注意です。
decodeのメソッドですが、nilの場合はエラーになるので、nilを考慮した作りが必要です。
ちなみにnilをセットした時、動きが違いました。
1 2 3 4 5 6 7 8 9 10 11 12 |
dalli.set(:nil, nil) # nilをセット dalli.get(:nil) # nilが取得される。 redis.set(:nil, nil) # nilをセット dalli.get(:nil) # 空文字が取得される redis.set(:nil, encode(nil)) # エンコードしてnilをセット decode(redis.get(:nil)) # デコードするとnilで取得可能 decode(nil) # これは上記の定義ではエラーが発生します。 # nilの時はnilを返すような仕様にするか、 # nil参照のエラーにするかはアプリ次第? |
速度差
KVSなんで、高速の読み書きが求められます。
今回は3秒間でどれだけ読み書き(KVSへの値セットと値取得を1回ずつ行う)ができるかを測定します。
memcached
1 2 3 4 5 6 7 8 9 10 11 12 |
count = 0 begin timeout(3) { loop { dalli.set(count, count) dalli.get(count) count = count + 1 } } ensure p count end |
redis
1 2 3 4 5 6 7 8 9 10 11 12 |
count = 0 begin timeout(3) { loop { redis.set(count, encode(count)) decode(redis.get(count)) count = count + 1 } } ensure p count end |
結果は下記の通り
10回の平均です。
3秒間での読み書き回数 | |
---|---|
memcached | 19958.5 |
redis | 19627.5 |
redis コンバートなし | 20638.5 |
DB postgres | 956.5 |
ハッシュ | 3402237.3 |
KVSだけではそれほど差がなかったので、DBとハッシュも比較対象に追加してみました。
今回はストレージがローカルにあるのでこの数字が出ましたが、ネットワーク越しにあれば、もう少し速度は低下すると思います。
速度は
メモリ > KVS > DB
でそれぞれ200倍ほどの差があるみたいですね。
当然ちゃ当然の結果ですが。
KVSを使うことがあれば参考にしてください。