【初心者向け】Redisのデータ型とPythonでの使い方サンプル

Redisのデータ型とPythonでの使い方サンプルです。[言語処理100本ノック](http://www.cl.ecei.tohoku.ac.jp/nlp100/)をやっていて、第7章でKVSとしてRedisを使いました。そのときに学習したことのメモです。
**NoSQLは使い所も多く学習価値が高い**と考えたので[「RDB技術者のためのNoSQLガイド」](https://amzn.to/2HOijWj)を購入してやや比重を重くして勉強しました。RDBと比べて考え方が斬新で面白かったです(非常にわかりやすく、初心者にありがたい本でした)。

# 環境
インストールに関しては記事[「最新RedisのUbuntuへのインストールとPythonで使うまで」](https://qiita.com/FukuharaYohei/items/4485d1b11f88fd4d1f55)を参照ください。


|種類|バージョン|内容|
|:--|:--|:--|
|OS|Ubuntu18.04.01 LTS|仮想で動かしています|
|Redis|5.0.4|2019/3/22時点でStableの最新です|
|pyenv|1.2.9|複数Python環境を使うことがあるのでpyenv使っています|
|Python|3.7.2|pyenv上でpython3.7.2を使っています<br>パッケージはvenvを使って管理しています|

# 参考リンク

[RDB技術者のためのNoSQLガイド](https://amzn.to/2HOijWj)
[公式 Documentation - Data types](https://redis.io/topics/data-types)
[公式チュートリアル](https://try.redis.io/)
[SlideShare : Redisの特徴と活用方法について](https://www.slideshare.net/yujiotani16/redis-76504393)
[SlideShare : webエンジニアのためのはじめてのredis](https://www.slideshare.net/ssusera66c10/webredis)
[PrestoとHyperLogLogで、大量ログからユニークユーザー数を高速に推定する（理論編）](http://blog.brainpad.co.jp/entry/2016/06/27/110000)


# データ型
## 一覧

|データ型|概要|イメージ図|
|:-:|:--|:-:|
|Strings|文字型<br>KeyとValueが1:1のシンプル構造|![RedisDataType01String.jpg](https://qiita-image-store.s3.amazonaws.com/0/134273/f799195e-ad29-0f76-1ce6-f4ee6d4fa88a.jpeg)|
|Lists|順序ありの文字型リスト(重複許容)<br>左端と右端を持ち、要素に対してどちらかから追加/削除を実行<br>SNSやブログのタイムラインに使われる|![RedisDataType02List.jpg](https://qiita-image-store.s3.amazonaws.com/0/134273/ef7426b1-0115-b299-c375-f68256ca11fe.jpeg)|
|Sets|順序、重複なしの文字型集合<br>タギングに使われる|![RedisDataType03Sets.jpg](https://qiita-image-store.s3.amazonaws.com/0/134273/621fb425-7ff8-1999-d6c2-d13a97716691.jpeg)|
|Sorted Sets|重複なしScoreによる順序ありの文字型集合<br>Scoreの値を使って特定が可能|![RedisDataType04SortedSets.jpg](https://qiita-image-store.s3.amazonaws.com/0/134273/e576bcd3-8144-c244-7384-464b195f2ec2.jpeg)|
|Hashes|重複なく、順序のない値とキーをセットで持つ|![RedisDataType05Hashed.jpg](https://qiita-image-store.s3.amazonaws.com/0/134273/efae77e7-1c4f-ed31-ac22-b4589552f013.jpeg)|
|Bitmap|0か1のbit集合を扱うためのデータ型|なし|
|HyperLogLog|大量データに対してユニークな要素数を高速に推定するためのデータ型|なし|


## [1. Strings](https://redis.io/topics/data-types-intro#redis-strings)
Stringは基本の文字型。インクリメントもできる。
[GitHub link : 10.DataString.ipynb](https://github.com/YoheiFukuhara/redis-test/blob/master/10.DataString.ipynb)

```python:10.DataString.ipynb
from redis import Redis
r = Redis( )

# Keyを"key"にしてvalue"foo"を登録
r.set("key", "foo")
print(r.get("key"))
#=>b'foo'

# Keyを"test_int"にしてvalue 10を登録
r.set("test_int",10)

# インクリメントできる
r.incr("test_int")
print(r.get("test_int"))
#=>b'11'

# 削除
r.flushall()
```

## [2. Lists](https://redis.io/topics/data-types-intro#redis-lists)
単純なStringsのリスト型。重複可能で順序性あり。
[GitHub link : 20.DataList.ipynb](https://github.com/YoheiFukuhara/redis-test/blob/master/20.DataList.ipynb)

```python
from redis import Redis
r = Redis( )

# Listに値を追加(重複確認のためAを2回追加)
r.rpush("mylist", "A")
r.rpush("mylist", "B")
r.rpush("mylist", "A")

# Listの最初に値を追加
r.lpush("mylist", "first")

#　Listを0(最初)から-1(全部)まで照会
r.lrange("mylist", 0, -1)
#=> [b'first', b'A', b'B', b'A']

#　Listを1(2つ目)から2つまで照会
r.lrange("mylist", 1, 2)
#=> [b'A', b'B']

# 削除
r.flushall()
```

## [3. Sets](https://redis.io/topics/data-types-intro#redis-sets)
順序性がなく重複不可な文字型集合。
[GitHub link : 30.DataSets.ipynb](https://github.com/YoheiFukuhara/redis-test/blob/master/30.DataSets.ipynb)

```python
from redis import Redis
r = Redis( )

# Setsに1, 2, 3を追加
r.sadd("myset", 1, 2, 3)
r.smembers("myset")
#=> {b'1', b'2', b'3'}

# 重複不可なので1を追加しようとしてもできない(return=0)
print(r.sadd("myset", 1))
#=> 0

# 削除
r.flushall()
```

## [4.Sorted Sets](https://redis.io/topics/data-types-intro#redis-sorted-sets)
Scoreというソートキーを内部的に持っているSets。重複不可。
[GitHub link : 40.DataSortedSets.ipynb](https://github.com/YoheiFukuhara/redis-test/blob/master/40.DataSortedSets.ipynb)

```python
from redis import Redis
r = Redis( )

# 辞書型の作成
dict = {}
dict["Alan Kay"]         = 1940
dict["Sophie Wilson"]    = 1957
dict["Richard Stallman"] = 1953

# 追加
r.zadd("hackers", dict)

# 参照(Scoreなし)
r.zrange("hackers",0,-1)
#=> [b'Alan Kay', b'Richard Stallman', b'Sophie Wilson']

# 参照(Scoreあり)
r.zrange("hackers",0,-1, withscores=True)
#=> [(b'Alan Kay', 1940.0),
#=>  (b'Richard Stallman', 1953.0),
#=>  (b'Sophie Wilson', 1957.0)]

# ちなみに、同じKeyで別値をzaddすると値上書き
dict2 = {}
dict2["Alan Kay"]         = 19401
r.zadd("hackers", dict2)

# 削除
r.flushall()
```

## [5. Hashes](https://redis.io/topics/data-types-intro#redis-hashes)
順序のない値とセットをキーで保持。重複不可。
[GitHub link : 50.DataHash.ipynb](https://github.com/YoheiFukuhara/redis-test/blob/master/50.DataHash.ipynb)

```python
from redis import Redis
r = Redis( )

#　1Valueをセット
r.hset("user:1000", "username", "antirez")
r.hgetall("user:1000")
#=> {b'username': b'antirez'}

# まとめてセット
dict = {}
dict["birthyear"]   = 1977
dict["verified"]    = 1
r.hmset("user:1000",dict)
r.hgetall("user:1000")
#=> {b'username': b'antirez', b'birthyear': b'1977', b'verified': b'1'}

# ちなみに、同じKeyで別値をセットすると値上書き
r.hset("user:1000", "username", "antirez2")
r.hgetall("user:1000")
#=> {b'username': b'antirez2', b'birthyear': b'1977', b'verified': b'1'}

# 削除
r.flushall()
```

# 主なクエリ一覧

|コマンド|redis-pyコマンド|説明|
|:-:|:---|:---|
|[SET](https://redis.io/commands/set)|[set(name, value, ex=None, px=None, nx=False, xx=False)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.set)|Keyに対するValueを指定して新規格納/上書き|
|[GET](https://redis.io/commands/get)|[get(name)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.get)|Keyに対するValueを取得|
|[DEL](https://redis.io/commands/del)|[delete(*names)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.delete)|Keyに対するValueを削除|
|[INCR](https://redis.io/commands/incr)|[incr(name, amount=1)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.incr)|Keyに対してValueを1インクリメント|
|[MSET](https://redis.io/commands/mset)|[mset(mapping)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.mset)|SETを複数一括処理|
|[MGET](https://redis.io/commands/mget)|[mget(keys, *args)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.mget)|GETを複数一括処理|
|[LPUSH](https://redis.io/commands/lpush)|[lpush(name, *values)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.lpush)|Keyに対するListの左端に要素追加|
|[RPUSH](https://redis.io/commands/rpush)|[rpush(name, *values)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.rpush)|Keyに対するListの右端に要素追加|
|[LLEN](https://redis.io/commands/llen)|[llen(name)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.llen)|Keyに対するList長取得|
|[LRANGE](https://redis.io/commands/lrange)|[lrange(name, start, end)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.lrange)|Keyに対する開始と終了位置を指定したListの要素取得|
|[LPOP](https://redis.io/commands/lpop)|[lpop(name)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.lpop)|Keyに対するList左端からの要素取得|
|[RPOP](https://redis.io/commands/rpop)|[rpop(name)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.rpop)|Keyに対するList右端からの要素取得|
|[SADD](https://redis.io/commands/sadd)|[sadd(name, *values)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.sadd)|Setsに対する要素追加|
|[SREM](https://redis.io/commands/srem)|[srem(name, *values)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.srem)|Setsに対しての指定した要素削除|
|[SISMEMBER](https://redis.io/commands/sismember)|[sismember(name, value)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.sismember)|Setsに対しての要素存在確認|
|[SMEMBERS](https://redis.io/commands/smembers)|[smembers(name)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.smembers)|Setsに対しての要素全取得|
|[SUNION](https://redis.io/commands/sunion)|[sunion(keys, *args)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.sunion)|複数のSets結合結果取得(重複除外されている)|
|[ZADD](https://redis.io/commands/zadd)|[zadd(name, mapping, nx=False, xx=False, ch=False, incr=False)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.zadd)|Sorted Setsに要素追加|
|[ZRANGE](https://redis.io/commands/zrange)|[zrange(name, start, end, desc=False, withscores=False, score_cast_func=<type 'float'>)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.zrange)|Sorted Setsに対する開始と終了位置を指定した要素取得|
|[HSET](https://redis.io/commands/hset)|[hset(name, key, value)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.hset)|Hashesに対する要素追加|
|[HGETALL](https://redis.io/commands/hgetall)|[hgetall(name)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.hgetall)|Hashesの要素を全取得|
|[HMSET](https://redis.io/commands/hmset)|[hmset(name, mapping)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.hmset)|HSETを複数一括処理|
|[HGET](https://redis.io/commands/hget)|[hget(name, key)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.hget)|Hashesに対して指定したフィールドのValue取得|
|[HINCRBY](https://redis.io/commands/hincrby)|[hincrby(name, key, amount=1)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.hincrby)|Hashesに対して指定したフィールドのValueを1インクリメント|
|[HDEL](https://redis.io/commands/hdel)|[hdel(name, *keys)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.hdel)|Hashesに対して指定したフィールドとValue削除|
|[EXPIRE](https://redis.io/commands/expire)|[expire(name, time)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.expire)|Keyと秒数を指定して有効期限設定|
|[TTL](https://redis.io/commands/ttl)|[ttl(name)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.ttl)|Keyを指定して有効期限取得|
|[INFO](https://redis.io/commands/info)|[info(section=None)](https://redis-py.readthedocs.io/en/latest/index.html#redis.Redis.info)|Reidsサーバの状態・統計情報等取得|