先日、ISUCONの練習としてISHOCONに参加してきました。ISUCONもそうですが初期実装のRDBMS部分をどれだけRedis等に移せるかでスコアの伸び方が違う気がします。少なくとも今回の問題はRedisを上手に使えるかが鍵です(少なくともPythonでは
しかし、イマイチRedisを触ったことがなかったのでまとめておきます。
Redis
Wikipediaより
Redisは、データ構造サーバーを実装するオープンソースソフトウェアプロジェクトである。 いわゆるNoSQLデータベースの一つであり、Redis Labsがスポンサーとなって開発されている。 ネットワーク接続されたインメモリデータベースでかつキー・バリュー型データベースであり、オプションとして永続性を持つ。
売りとしてはマスタスレーブだったりってのもありますが、1ノードなのでただのおっきなDictとして使い潰します。memcachedとくらべて、バリューに配列やハッシュが使えるのが特徴らしいです。
使い方
redis-pyのインストール
Pythonからredisを使うにはredis-pyを使います。
$ pip install redis
基本操作
接続
以下の様に接続します。
import redis r = redis.StrictRedis(host='localhost', port=6379, db=0)
また、コネクションプールもあるようです。
pool = redis.ConnectionPool(host='localhost', port=6379, db=0) r = redis.StrictRedis(connection_pool=pool)
手元の環境では、プールを使うほうが2倍程度接続が早かったです。
condition | time |
---|---|
プールなし | 9.07 µs ± 203 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) |
プールあり | 4.04 µs ± 123 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) |
データの保存・取り出し
KVSなので、原則はKeyを指定してValueをしまう、です。KeyとValueは1:1なので2回setした場合はあとから行ったデータで上書きされます。
r.set("KEY", "VALUE")
取得時はget
を使います。
r.get("KEY") # --> VALUEが戻る
リスト
Valueの部分が配列として管理されます。一つのキーに複数まとめることができます。RDBMSに慣れている場合はキーがテーブルでValueに入るデータが行のようなイメージでしょうか。以下でKey=LISTに10000個のデータが登録されます。lpush
であれば先頭へ、rpush
であれば末尾に追加されます。
for i in range(10000): r.lpush("LIST_lpush", "DATA{}".format(i)) r.rpush("LIST_rpush", "DATA{}".format(i))
取り出す際はlrange
を使用します。範囲を指定しますが負数も可能です。
r.lrange('LIST_lpush', 0, 0) # -> [b'DATA9999',] r.lrange('LIST_lpush', -2,-1) # -> [b'DATA1', b'DATA0'] r.lrange('LIST_rpush', 0, 0) # -> [b'DATA0'] r.lrange('LIST_rpush', -2,-1) # -> [b'DATA9998', b'DATA9999']
若干ハマるのが、Pythonのrange()
等はstart, endを指定した場合end自体は含まないですが、Redisの場合はendまでを含むので取得数に注意します。
また、リスト型はllen
で長さを取得することが可能です。
r.llen('LIST_rpush') # -> 10000
ハッシュ型
Valueの部分をPythonのDictのような形で持たせることができます。hset
で値を設定します。他のset系と異なり、通常のキーに加えてハッシュ型のキーも渡してからセットする値を指定します。
r.hset("parent", "child1", "value1") r.hset("parent", "child2", "value2") r.hset("parent", "child3", "value3")
この例では
parent: { "child1": "value1", "child2": "value2", "child3": "value3", }
のような形のデータが保存されます。
取得する際は、以下の様に2つのキーを指定します。
r.hget("parent", "child1") # -> "value1"が戻る
さらに、hgetall
でキー配下のすべてのデータを取得できます。
r.hgetall("parent") # -> {b'child1': b'value1', b'child2': b'value2', b'child3': b'value3'}
リストよりも複雑なデータを扱う場合は有効だと思います。
まとめ
redis-pyについて基本的な使い方をまとめました。次回は、ISHOCONで使用したもう少し実践的な話やパフォーマンス面についてまとめたいと思います。