[Dd]enzow(ill)? with DB and Python

DBとか資格とかPythonとかの話をつらつらと

PythonでRedisを扱う(redis-pyの基本)

先日、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で使用したもう少し実践的な話やパフォーマンス面についてまとめたいと思います。