Redisの概要とNode.jsでの使い方

# Redisの概要
redisはインメモリのNoSQLのデータベースです。単純なKey-Valueストアのように文字列だけでなく、様々な種類の値をサポートしています。
Webアプリケーションのスケーリングに便利です。

https://redis.io/

## 環境構築
dockerが利用できるなら、それで環境を作成するのが楽です。

```コマンド実行例
 docker run --name redis -p 6379:6379 -d -v /Users/hoge/dev/node/socketio/data:/data　redis:latest
```

dockerで作成したコンテナでredis-cliを使用するには以下のようにします。

```
hoge@macpro socketio % docker exec -it redis /bin/bash
root@8f9888c8bb76:/data# redis-cli
127.0.0.1:6379> 
```

ホストとポートを指定したい場合は以下のようにします。デフォルトではlocal:6379に接続しています。

```
redis-cli -h localhost -p 6379
```

## Redisのデータについて
Redisは単純なキーと値だけでなくリストやハッシュなどが使えます。
この章は以下を元に記述されていますので英語読める人は以下読んだほうがいいと思います。
https://redis.io/topics/data-types-intro
https://redis.io/topics/data-types

また、この章で記述されているコマンドはredis-cliで実行したものとなります。

### キー
Redisのキーはバイナリデータを正しく扱うことができます。"foo"のような文字列からJPEGファイルの中身まで任意のバイナリをキーとして使えます。空の文字列も有効なキーです。
キーに関するルールは以下の通りです。

 - 非常に長いキーはお勧めしません。例えば1024バイトのキーはメモリの点だけでなく、データセット内の検索にコストがかかります。
 - 多くの場合、非常に短いキーはお勧めしません。"user:1000:followers"と記載する代わりに"u1000flw"とかく意味はほとんどありません。前者は読みやすく、使用されるスペースもわずかです。
 - スキーマーに固執するようにしましょう。とえば、"user:1000"のように、 "object-type:id"のような形式にするのは良いアイディアです。
　キーの最大は512MBです。

#### キーに関係するコマンド

|コマンド名|概要|
|:---|:---|
|[DEL](https://redis.io/commands/del)|キーを削除する|
|[UNLINK](https://redis.io/commands/unlink)|別スレッドで非同期にキーを削除する。DELと同じだが非ブロッキング。|
|[EXISTS](https://redis.io/commands/exists)|キーが存在するか確認する|
|[TYPE](https://redis.io/commands/type)|キーに格納されている値のタイプを取得する|
|[KEYS](https://redis.io/commands/keys)|指定のパターンに一致するキーの一覧を取得する|
|[TOUCH](https://redis.io/commands/touch)|キーの最終アクセス時刻を変更する|
|[RENAME](https://redis.io/commands/rename)|キーの名前を変更する|
|[RENAMENX](https://redis.io/commands/renamenx)|新しいキーが存在しない場合のみキーの名前を変更する|
|[DUMP](https://redis.io/commands/dump)|指定されたキーに格納されている値のシリアル化されたバージョンを取得する|
|[OBJECT](https://redis.io/commands/object)|Redisオブジェクトの内部を検査する|
|[MOVE](https://redis.io/commands/move)|キーを別のデーターベースに移動する|
|[MIGRATE](https://redis.io/commands/migrate)|キーをRediusインスタンスから別のインスタンスに転送する|
|[RANDOMKEY](https://redis.io/commands/randomkey)|キースペースからランダムなキーを返す|
|[RESTORE](https://redis.io/commands/restore)|以前にDUMPしたシリアル化された値を使用してキーを作成する|
|[SORT](https://redis.io/commands/sort)|リスト、セット、ソート済セットの要素を並び替える|
|[WAIT](https://redis.io/commands/wait)|別の接続コンテキストで送信された全ての書き込みコマンドの同期複製を待つ|
|[SCAN](https://redis.io/commands/scan)|キースペースを段階的に反復する|



### 文字列
文字列は最も基本的な種類のRedis値です。Redis文字列はバイナリを正しく扱うことができ、単純な文字列から、JPEG画像など、あらゆる種類のデータを含めることができます。文字列値の最大は512MBです。


#### 単純なサンプル
[SETコマンド](https://redis.io/commands/set)と[GETコマンド](https://redis.io/commands/get)を使用することで文字列の値を格納、取得することができます。

redis-cliを使用して簡単なキーと値を格納するサンプルを以下に示します。


```text:使用例
 localhost:6379> set mykey somevalue
 OK
 localhost:6379> get mykey
 "somevalue"
 localhost:6379> 
```

規定では[SETコマンド](https://redis.io/commands/set)ではキーがすでに存在している場合は、既存のキーに格納されている値を新しい文字列に置き換えることができます。
すでに存在するキーの場合は失敗させる、または存在するキーの場合のみ更新させる場合にはnxオプション、xxオプションを以下のようにして使用します。

```text:使用例
 すでに存在するキーの場合は失敗させる
 localhost:6379> set mykey newval nx
 (nil)
 
 すでに存在するキーがある場合のみ成功させる
 localhost:6379> set mykey newval xx
 OK
 ```

[GETコマンド](https://redis.io/commands/get)ではキーが存在しない場合はnilを返します。また、格納されている値が文字列ではない場合、エラーとなります。

#### 文字列に関係するコマンド
文字列に関係するコマンドは以下の通りです。
https://redis.io/commands/#string

|コマンド名|概要|
|:---|:---|
|[APPEND](https://redis.io/commands/append)|指定のキーの値に指定の文字列を追記する|
|[GET](https://redis.io/commands/get)|指定のキーの文字列値を取得する|
|[SET](https://redis.io/commands/set)|キーの文字列値を設定する|
|[SETEX](https://redis.io/commands/setex)|有効期限を秒で指定してキーの文字列値を設定する|
|[SETNX](https://redis.io/commands/setnx)|キーが存在しない場合にキーの文字列値を設定する|
|[PSETEX](https://redis.io/commands/psetex)|有効期限をミリ秒で指定してキーの文字列値を設定する|
|[GETSET](https://redis.io/commands/getset)|キーの文字列値を設定し、古い値を取得する|
|[MGET](https://redis.io/commands/mget)|指定された複数のキーの値を取得する|
|[MSET](https://redis.io/commands/mset)|指定された複数のキーと値を設定する|
|[MSETNX](https://redis.io/commands/msetnx)|キーが存在しない複数のキーと値を設定する|
|[GETRANGE](https://redis.io/commands/getrange)|指定のキーの値の部分文字列を取得する|
|[SETRANGE](https://redis.io/commands/setrange)|指定のキーの値の部分文字列を設定する|
|[STRLEN](https://redis.io/commands/strlen)|キーの値の文字列の長さを取得する|


##### APPENDコマンド
指定のキーの値に指定の文字列を追記します。

```text:使用例
127.0.0.1:6379> set mykey hello
OK
127.0.0.1:6379> append mykey world
(integer) 10
127.0.0.1:6379> get mykey
"helloworld"
```

##### GETSETコマンド
GETSETコマンドを使用することで新しい値を設定して、古い値を取得することが可能です。

```text:使用例
 localhost:6379> set mykey test
 OK
 localhost:6379> getset mykey testnew
 "test"
 localhost:6379> get mykey
 "testnew"
```

##### MSETとMGETコマンド
複数のキーの値を同時に設定、取得するには[MSET](https://redis.io/commands/mset),[MGET](https://redis.io/commands/mget)コマンドを使用します。

```text:使用例
 localhost:6379> mset a 10 b 30 c 30
 OK
 localhost:6379> mget a b c
 1) "10"
 2) "30"
 3) "30"
 localhost:6379> get a
 "10"
 localhost:6379> get b
 "30"
 localhost:6379> get c
 "30"
```



#### 数値として取り扱うコマンド
rediusの文字列値が整数または浮動小数として変換できる場合は以下のコマンドが使用できます。

|コマンド名|概要|
|:---|:---|
|[INCR](https://redis.io/commands/incr)|キーの整数値を１増やす|
|[INCRBY](https://redis.io/commands/incrby)|キーの整数値を指定された数増やす|
|[DECR](https://redis.io/commands/decr)|キーの整数値を１減らす|
|[DECRBY](https://redis.io/commands/decrby)|キーの整数値を指定された数減らす|
|[INCRBYFLOAT](https://redis.io/commands/incrbyfloat)|キーのfloat値を指定された数増やす|

これらのコマンドはアトミックな操作となっており、同じキーに対して複数のクライアントが同時に操作しても競合状態にはなりません。例えばINCRコマンドを使用してクライアントAが「10」を読み取り、同時にクライアントBが「10」を読み込んで、両方とも「11」に設定することはなく、最終値は常に「12」になります。

```text:使用例
 localhost:6379> set counter 100
 OK
 localhost:6379> incr counter
 (integer) 101
 localhost:6379> incr counter
 (integer) 102
 localhost:6379> incr counter
 (integer) 103
 localhost:6379> set counter 100
 OK
 localhost:6379> incrby counter 5
 (integer) 105
 localhost:6379> decr counter
 (integer) 104
 localhost:6379> decrby counter 5
 (integer) 99
```

### リスト
Redisリストは、挿入順で並べ替えられた、文字列のリストです。リストに対して先頭（左）または後方（右）に値を追加できます。
リストの最大長は4294967295です。

#### 単純なサンプル
[RPUSHコマンド](https://redis.io/commands/rpush)でリストの後方に文字列を追加し、[LPUSHコマンド](https://redis.io/commands/lpush)でリストの先頭に文字列を取得します。リストの内容を取得するには[LRANGEコマンド](https://redis.io/commands/lrange)で範囲を指定して取得します。


```text:使用例
127.0.0.1:6379> rpush mylist AA
(integer) 1
127.0.0.1:6379> rpush mylist BB
(integer) 2
127.0.0.1:6379> lpush mylist first
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "first"
2) "AA"
3) "BB"
```

#### リストに関係するコマンド
https://redis.io/commands#list

|コマンド名|概要|
|:---|:---|
|[BLPOP](https://redis.io/commands/blpop)|リストの最初の要素を削除して取得するまたは、要素が使用可能になるまでブロックする|
|[BRPOP](https://redis.io/commands/brpop)|リストの最後の要素を削除して取得するまたは、要素が使用可能になるまでブロックする|
|[BRPOPLPUSH](https://redis.io/commands/brpoplpush)|リストから要素をポップして、それを別のリストにプッシュして返す。またはいずれかが利用可能になるまでブロックする|
|[LINDEX](https://redis.io/commands/lindex)|インデックスでリストから要素を取得する|
|[LINSERT](https://redis.io/commands/linsert)|リスト内の別の要素の前後に要素を挿入する|
|[LLEN](https://redis.io/commands/llen)|リストの長さを取得する|
|[LPOP](https://redis.io/commands/lpop)|リストの最初の要素を削除して取得する|
|[LPOS](https://redis.io/commands/lpos)|リストの一致する要素のインデックスを取得する|
|[LPUSH](https://redis.io/commands/lpush)|リストに１つまたは複数の要素をリストの先頭に追加する|
|[LPUSHX](https://redis.io/commands/lpushx)|リストが存在する場合のみ、リストの先頭に要素を追加する|
|[LRANGE](https://redis.io/commands/lrange)|リストから指定の範囲の要素を取得する|
|[LREM](https://redis.io/commands/lrem)|リストから要素を削除する|
|[LSET](https://redis.io/commands/lset)|リスト内の要素の値をインデックスで指定する|
|[LTRIM](https://redis.io/commands/ltrim)|リストを指定された範囲でトリムします。（指定した範囲外の要素を削除する）|
|[RPOP](https://redis.io/commands/rpop)|リストの最後の要素を削除して取得する|
|[RPOPLPUSH](https://redis.io/commands/rpoplpush)|リストの最後の要素を削除して別のリストの前に追加して返す|
|[RPUSH](https://redis.io/commands/rpush)|リストの末尾に１つまたは複数の要素を追加する|
|[RPUSHX](https://redis.io/commands/rpushx)|リストが存在する場合にのみ、リストに要素を追加する|


### セット
文字列の順序付けられていないコレクションです。
コレクション内のメンバーは同じメンバーを許可しないという性質を持っています。
セット内のメンバーの数は最大4294967295になります。

#### 簡単なサンプル
セットを使用するには[SADDコマンド](https://redis.io/commands/sadd)で新しい要素を追加して、[SMEMBERSコマンド](https://redis.io/commands/smembers)でセットの要素を取得します。

```
127.0.0.1:6379> sadd myset 3 1 2 3
(integer) 3
127.0.0.1:6379> smembers myset
1) "1"
2) "2"
3) "3"
```

この例では重複する要素が登録されていないことをが確認できます。
Redisのセットでは要素の順番については保証がないため、SMEMBERSコマンドは呼び出しのたびに任意の順番で返却されます。

#### セット用コマンド
https://redis.io/commands#set

|コマンド名|概要|
|:---|:---|
|[SADD](https://redis.io/commands/sadd)|セットにメンバーを追加する|
|[SCARD](https://redis.io/commands/scard)|セットのメンバー数を取得する|
|[SDIFF](https://redis.io/commands/sdiff)|複数のセットの差を取得する|
|[SDIFFSTORE](https://redis.io/commands/sdiffstore)|複数のセットの差を取得して結果セットをキーに格納する|
|[SINTER](https://redis.io/commands/sinter)|複数のセットを交差させる|
|[SINTERSTORE](https://redis.io/commands/sinterstore)|複数のセットを交差させ、その結果セットをキーに格納する|
|[SISMEMBER](https://redis.io/commands/sismember)|指定された値がセットのメンバーであるか判定する|
|[SMEMBERS](https://redis.io/commands/smembers)|セット内の全てのメンバーを取得する|
|[SMOVE](https://redis.io/commands/smove)|メンバーをあるセットから別のセットに移動する|
|[SPOP](https://redis.io/commands/spop)|セットから１つまたは複数のメンバーをランダムに削除して返す|
|[SRANDMEMBER](https://redis.io/commands/srandmember)|セットから１つまたは複数のメンバーをランダムに取得する|
|[SREM](https://redis.io/commands/srem)|セットから１つ以上のメンバーを削除する|
|[SUNION](https://redis.io/commands/sunion)|複数のセットを追加する|
|[SUIONSCTORE](https://redis.io/commands/sunionstore)|複数のセットを追加して、結果のセットをキーに格納する|

##### SDIFFとSINTERコマンドのサンプル
SINTERコマンドは最初に指定したセットと後に指定したセットと同じメンバーを取得します。
SDIFFコマンドでは最初に指定したセットから、共有するメンバーを除いたメンバーを返します。


```
127.0.0.1:6379> sadd key1 a b c
(integer) 3
127.0.0.1:6379> sadd key2 c d e
(integer) 3
127.0.0.1:6379> sdiff key1 key2
1) "a"
2) "b"
127.0.0.1:6379> SINTER key1 key2
1) "c"
```

### ソート済セット
Redisセットと同様に、文字列の繰り返しのないコレクションです。違いは、並べ替えられたセットのすべてのメンバーがスコアに関連付けられていることです。これは、並べ替えられたセットを最小から最大のスコアまで順番に並べるために使用されます。メンバーは一意ですが、スコアは繰り返される場合があります。

#### 簡単なサンプル
[ZADDコマンド](https://redis.io/commands/zadd)を使用してスコアと値を指定してソート済セットに追加します。
ソート済セットの内容は[ZRANGEコマンド](https://redis.io/commands/zrange)を使用してスコアの小さい順で取得することができます。この際、withscoresオプションを使用するとスコアも同時に取得することができます。
スコアの大きいの順番で取得するには[ZREVRANGEコマンド](https://redis.io/commands/zrevrange)を取得できます。

```
127.0.0.1:6379> zadd hackers 1940 "Alan Kay"
(integer) 1
127.0.0.1:6379> zadd hackers 1957 "Sophie Wilson"
(integer) 1
127.0.0.1:6379> zadd hackers 1953 "Richard Stallman"
(integer) 1
127.0.0.1:6379> zadd hackers 1949 "Anita Borg"
(integer) 1
127.0.0.1:6379> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
127.0.0.1:6379> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
127.0.0.1:6379> zadd hackers 1916 "Claude Shannon"
(integer) 1
127.0.0.1:6379> zadd hackers 1969 "Linus Torvalds"
(integer) 1
127.0.0.1:6379> zadd hackers 1912 "Alan Turing"
(integer) 1
127.0.0.1:6379> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
6) "Richard Stallman"
7) "Sophie Wilson"
8) "Yukihiro Matsumoto"
9) "Linus Torvalds"
127.0.0.1:6379> zrange hackers 0 -1 withscores
 1) "Alan Turing"
 2) "1912"
 3) "Hedy Lamarr"
 4) "1914"
 5) "Claude Shannon"
 6) "1916"
 7) "Alan Kay"
 8) "1940"
 9) "Anita Borg"
10) "1949"
11) "Richard Stallman"
12) "1953"
13) "Sophie Wilson"
14) "1957"
15) "Yukihiro Matsumoto"
16) "1965"
17) "Linus Torvalds"
18) "1969"
127.0.0.1:6379> zrevrange hackers 0 -1
1) "Linus Torvalds"
2) "Yukihiro Matsumoto"
3) "Sophie Wilson"
4) "Richard Stallman"
5) "Anita Borg"
6) "Alan Kay"
7) "Claude Shannon"
8) "Hedy Lamarr"
9) "Alan Turing"
```

#### ソート済セットで使用するコマンド

|コマンド名|概要|
|:---|:---|
|[BZPOPMIN](https://redis.io/commands/bzpopmin)|１つ以上のソート済セットからスコアが最も低いメンバーを削除して取得するまたは、使用可能になるまでブロックする|
|[BZPOPMAX](https://redis.io/commands/bzpopmax)|１つ以上のソート済セットからスコアが最も高いメンバーを削除して取得するまたは、使用可能になるまでブロックする|
|[ZADD](https://redis.io/commands/zadd)|ソート済セットに１つ以上のメンバーを追加するか、すでに存在する場合はそのスコアを更新する|
|[ZCARD](https://redis.io/commands/zcard)|ソート済セットのメンバー数を取得する|
|[ZCOUNT](https://redis.io/commands/zcount)|指定されたスコアの範囲でソート済セットのメンバー数を取得する|
|[ZINCRBY](https://redis.io/commands/zincrby)|ソート済のメンバーのスコアをインクリメントする|
|[ZINTERSTORE](https://redis.io/commands/zinterstore)|複数のソート済セットを交差させ、結果のソート済セットに新しい値を格納する|
|[ZLEXCOUNT](https://redis.io/commands/zlexcount)|与えられた辞書識範囲内でソート済メンバー数を取得する|
|[ZPOPMAX](https://redis.io/commands/zpopmax)|ソート済セットで最高スコアを持つメンバーを削除して取得する|
|[ZPOPMIN](https://redis.io/commands/zpopmin)|ソート済セットで最低スコアを持つメンバーを削除して取得する|
|[ZRANGE](https://redis.io/commands/zrange)|インデックスでソート済セットを取得する|
|[ZRANGEBYLEX](https://redis.io/commands/zrangebylex)|辞書式の範囲でソート済セットのメンバーを取得する|
|[ZREVRANGEBYLEX](https://redis.io/commands/zrevrangebylex)|辞書式の範囲でソート済セットのメンバーを上位から下位の文字列の順に取得する|
|[ZRANGEBYSCORE](https://redis.io/commands/zrangebyscore)|スコアの範囲でソート済のセットのメンバーを取得する|
|[ZRANK](https://redis.io/commands/zrank)|ソート済セットのインデックスを決定する|
|[ZREM](https://redis.io/commands/zrem)|ソート済セットから１つ以上のメンバーを削除する|
|[ZREMRANGEBYLEX](https://redis.io/commands/zremrangebylex)|指定された辞書式範囲でソート済セットの全てのメンバーを削除する|
|[ZREMRANGEBYRANK](https://redis.io/commands/zremrangebyrank)|指定されたインデックス内のソート済セットの全てのメンバーを削除する|
|[ZREMRANGEBYSCORE](https://redis.io/commands/zremrangebyscore)|指定されたスコア内のソート済セットの全てのメンバーを削除する|
|[ZREVRANGE](https://redis.io/commands/zrevrange)|ソート済セットをスコアの高い順に取得する|
|[ZREVRANGEBYSCORE](https://redis.io/commands/zrevrangebyscore)|スコアの範囲内でソート済セットをスコアの高い順に取得する|
|[ZREVRANK](https://redis.io/commands/zrevrank)|ソート済セットのインデックスを決定する。スコアは高い順に並べられる|
|[ZSCORE](https://redis.io/commands/zscore)|ソート済セット中のメンバーに関連づけられたスコアを取得する|
|[ZUNIONSTORE](https://redis.io/commands/zunionstore)|複数のソート済セットを追加して結果のソート済セットに新しいキーを追加する|
|[ZSCAN](https://redis.io/commands/zscan)|ソートされたセット要素と関連するスコアを段階的に反復する|

##### 辞書式スコア
[ZRANGEBYLEXコマンド](https://redis.io/commands/zrangebylex)ではソート済セットの要素が全て同じスコアで挿入されたと仮定して、範囲を辞書式で取得することができます。
辞書式の範囲の開始と終了を指定する際、「[」または「(」で始まる必要があります。
「[」の場合、後続の文字列を含んだ値を認めますが、「(」の場合は認めません。
また、「-」を指定すること負の方向で無限である文字列を現し、「+」を指定することで正に無限の文字列を現します。すなわち「ZRANGEBYLEX myzset - +」を指定した場合、全ての要素が同じスコアだとして、全てのメンバーを返します。

```
127.0.0.1:6379> ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g
(integer) 0
127.0.0.1:6379> ZRANGEBYLEX myzset - +
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
7) "g"
127.0.0.1:6379> ZRANGEBYLEX myzset - [f
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
6) "f"
127.0.0.1:6379> ZRANGEBYLEX myzset - (f
1) "a"
2) "b"
3) "c"
4) "d"
5) "e"
127.0.0.1:6379> ZRANGEBYLEX myzset [c (f
1) "c"
2) "d"
3) "e"
127.0.0.1:6379> ZRANGEBYLEX myzset (c (f
1) "d"
2) "e"
```

### ハッシュ
ハッシュは、フィールドと値のペアを使用してオブジェクトを表すことができます。

#### 簡単な例
[HSETコマンド](https://redis.io/commands/hset)を使用してキーに紐づくハッシュ値にフィールドを設定します。
[HGETALLコマンド](https://redis.io/commands/hgetall)を使用して指定のキーのハッシュの全てのフィールドとその値を取得します。
[HGETコマンド](https://redis.io/commands/hget)を使用することでキーとハッシュのフィールドを指定して、その文字列値を取得します。

```
127.0.0.1:6379> hset user:1000 username "Joe"
(integer) 1
127.0.0.1:6379> hset user:1000 birthyear 1945
(integer) 1
127.0.0.1:6379> hset user:1000 verified 1
(integer) 1
127.0.0.1:6379> hgetall user:1000
1) "username"
2) "Joe"
3) "birthyear"
4) "1945"
5) "verified"
6) "1"
127.0.0.1:6379> hget user:1000 username
"Joe"
127.0.0.1:6379> hget user:1000 birthyear
"1945"
127.0.0.1:6379> hget user:1000 verified
"1"

```

#### ハッシュに関係するコマンド

|コマンド名|概要|
|:---|:---|
|[HDEL](https://redis.io/commands/hdel)|1つい上のハッシュフィールドを削除する|
|[HEXISTS](https://redis.io/commands/hexists)|ハッシュフィールドが存在するか確認する|
|[HGET](https://redis.io/commands/hget)|ハッシュフィールドの値を取得する|
|[HGETALL](https://redis.io/commands/hgetall)|ハッシュの全てのフィールドと値を取得する|
|[HINCRBY](https://redis.io/commands/hincrby)|ハッシュフィールドの整数値を指定された数だけインクリメントする|
|[HINCRBYFLOAT](https://redis.io/commands/hincrbyfloat)|ハッシュフィールドのfloat値を指定された数だけインクリメントする|
|[HKEYS](https://redis.io/commands/hkeys)|ハッシュ内の全てのフィールドを取得する|
|[HLEN](https://redis.io/commands/hlen)|ハッシュのフィールド数を取得する|
|[HMGET](https://redis.io/commands/hmget)|指定された全てのハッシュフィールドの値を取得する|
|[HMSET](https://redis.io/commands/hmset)|複数のハッシュフィールドを複数の値に設定する|
|[HSET](https://redis.io/commands/hset)|ハッシュフィールドの文字列値を設定する|
|[HSETNX](https://redis.io/commands/hsetnx)|フィールドが存在しない場合のみ、ハッシュフィールドの値を設定する。|
|[HSTRLEN](https://redis.io/commands/hstrlen)|ハッシュフィールドの値の長さを取得する|
|[HVALS](https://redis.io/commands/hvals)|ハッシュの全ての値を取得する|
|[HSCAN](https://redis.io/commands/hscan)|ハッシュフィールドと関連する値を段階的に反復する|



### ビットマップ
#### ビット操作を行うコマンド
redisの文字列値はバイナリデータを取り扱うことができます。いくつかのコマンドはそのバイナリのビットを操作することが可能です。

|コマンド名|概要|
|:---|:---|
|[BITCOUNT](https://redis.io/commands/bitcount)|文字列中の1に設定されたビット数を数える|
|[BITFIELD](https://redis.io/commands/bitfield)|文字列に対して任意のビットフィールド整数演算を行う|
|[BITOP](https://redis.io/commands/bitop)|文字列間でビット演算を行う|
|[BITPOS](https://redis.io/commands/bitpos)|文字列の最初のビットセットまたはビットクリアを見つける|
|[GETBIT](https://redis.io/commands/getbit)|キーの文字列における指定のオフセットでのビットを取得する|
|[SETBIT](https://redis.io/commands/setbit)|キーの文字列における指定のオフセットでのビットを設定する|

### Redisキーの有効期限
Redisのキーには有効期限を設定することができます。特定の時間が経過するとキーはDELコマンドを実行した時のように自動的に破棄されます。

|コマンド名|概要|
|:---|:---|
|[EXPIRE](https://redis.io/commands/expire)|キーの存在時間を秒で設定する|
|[EXPIREAT](https://redis.io/commands/expireat)|キーの有効期限をUNIXタイムスタンプとして設定する|
|[PERSIST](https://redis.io/commands/persist)|キーから有効期限を削除する|
|[PEXPIRE](https://redis.io/commands/pexpire)|キーの有効期間をms単位で設定する|
|[PEXPIREAT](https://redis.io/commands/pexpireat)|キーの有効期限をmsで指定されたUNIXタイムスタンプとして設定する|
|[TTL](https://redis.io/commands/ttl)|キーの生存時間を秒単位で取得する|
|[PTTL](https://redis.io/commands/pttl)|キーの存在時間をms単位で取得する|

#### 単純なサンプル
有効期限の設定には以下のようにEXPIREコマンドを使用します。

```
 localhost:6379> set mykey test
 OK
 localhost:6379> expire mykey 5
 (integer) 1
 5秒以内・・・
 localhost:6379> get mykey
 "test"
 5秒経過・・・
 localhost:6379> get mykey
 (nil)
```

また同じことはSETコマンドのexオプションで指定することもできます。

```
 localhost:6379> set mykey sample ex 5
 OK
 localhost:6379> get mykey
 "sample"
 localhost:6379> get mykey
 (nil)
```

有効期限の残り秒数またはミリ秒を取得するには[TTL](https://redis.io/commands/ttl)コマンドまたは[PTTL](https://redis.io/commands/pttl)コマンドを使用します。

```
 localhost:6379> set mykey sample ex 10
 OK
 localhost:6379> pttl mykey
 (integer) 7982
 localhost:6379> ttl mykey
 (integer) 4
```

## Redis-cli
Redis-cliはRedisにコマンドを送信し、サーバーから送信された応答をターミナルから直接読み取ることができるシンプルなプログラムであるRedisコマンドラインインターフェイスです。

https://redis.io/topics/rediscli

redis-cli 6.0.8ではコマンドラインの引数として以下を設定できます。

|引数|説明|
|:---|:---|
|-h <hostname>|サーバーのホスト名 (default: 127.0.0.1).|
|-p <port>|サーバーのポート (default: 6379).|
|-s <socket>|サーバーのソケット(hostnameとportを上書きする)|
|-a <password>|サーバーに接続するときに使用するパスワード。 REDISCLI_AUTH環境変数を使用して、このパスワードをより安全に渡すこともできる。（両方が使用されている場合、この引数が優先される）|
|--user <username>|ACLスタイル'AUTH username pass'を送信するために使用される.-aが必要.|
|--pass <password>|新しい--userオプションと一貫性を保つために使用される-aのエイリアス|
|--askpass|STDINからマスク付きのパスワードを入力するようユーザーに強制する。もしこの引数が使われた場合, '-a' と　REDISCLI_AUTH環境変数は無視される|
|-u <uri>|サーバーURI.|
|-r <repeat>|特定のコマンドをN回繰り返す|
|-i <interval>|-rオプションを使用した場合にコマンドごとに <interval> 秒待機する。次のように1秒未満の時間を指定することが可能。例： 0.1.|
|-n <db>|データベースナンバー|
|-3|RESP3 protocol modeでセッションを開始する|
|-x|最後の引数をSTDINから読み込む|
|-d <delimiter>|Multi-bulk delimiter in for raw formatting (default: \n).|
|-c|クラスターモードを有効にする(follow -ASK and -MOVED redirections).|
|--tls|安全なTLS接続を確立します。|
|--sni <host>|TLSサーバー名を表示する|
|--cacert <file>|検証するCA証明書ファイル。|
|--cacertdir <dir>|信頼できるCA証明書が保存されているディレクトリ。cacertとcacertdirのどちらも指定されていない場合、デフォルトのシステム全体の信頼されたルート証明書の構成が適用される。|
|--cert <file>|認証に使用するクライアント証明書|
|--key <file>|認証に使用する秘密鍵ファイル|
|--raw|返信に未加工のフォーマットを使用します（STDOUTがttyでない場合のデフォルト）。|
|--no-raw|STDOUTがttyでない場合でもフォーマットされた出力を強制します。|
|--csv|CSV形式で出力する|
|--stat|server: mem, clientsなどの統計情報を出力し続ける|
|--latency|Enter a special mode continuously sampling latency.If you use this mode in an interactive session it runs forever displaying real-time stats. Otherwise if --raw or --csv is specified, or if you redirect the output to a non  TTY, it samples the latency for 1 second (you can use -i to change the interval), then produces a single output and exits.|
|--latency-history|--latencyと似ていますが、追跡の遅延は時間とともに変化する。デフォルトの時間間隔は15秒。 -iを使用して変更します。|
|--latency-dist|Shows latency as a spectrum, requires xterm 256 colors.Default time interval is 1 sec. Change it using -i.|
|--lru-test <keys>|Simulate a cache workload with an 80-20 distribution.|
|--replica|マスターから受信したコマンドを示すレプリカをシミュレートする|
|--rdb <filename>|RDBダンプをリモートサーバーからローカルファイルに転送する。|
|--pipe|raw Redisプロトコルをstdinからサーバーに転送します。|
|--pipe-timeout <n>|--pipeモードでは、すべてのデータの送信後、<n>秒以内に応答が受信されない場合はエラーで中止する。デフォルトのタイムアウト：30。永久に待機するには0を使用する。|
|--bigkeys|多くの要素（複雑さ）を持つキーを探すRedisキーのサンプル。|
|--memkeys|多くのメモリを消費するキーを探すRedisキーのサンプル。|
|--memkeys-samples <n>|大量のメモリを消費するキーを探すRedisキーのサンプル。サンプリングするキー要素の数を定義する|
|--hotkeys|ホットキーを探すサンプルRedisキー。maxmemory-policyが* lfuの場合にのみ機能します。|
|--scan|SCANコマンドを使用した全てのキーのリスト.|
|--pattern <pat>|--scan, --bigkeys or --hotkeys オプションを使用した場合のキーのパターン (default: *).|
|--intrinsic-latency <sec>|固有のシステム遅延を測定するテストを実行する。テストは指定された秒数実行される|
|--eval <file>|<file>でLuaスクリプトを使用してEVALコマンドを送信する|
|--ldb|--evalと共に使用すると、Redis Luaデバッガーが有効になる|
|--ldb-sync-mode|--ldbと同様ですが、同期Luaデバッガーを使用します。このモードでは、サーバーはブロックされ、スクリプトの変更はサーバーのメモリからロールバックされない|
|--cluster <command> [args...] [opts...]|Cluster Managerコマンドと引数|
|--verbose|Verboseモード.|
|--no-auth-warning|Don't show warning message when using password on command line interface.|
|--help|ヘルプの出力|
|--version|バージョンの出力|

### 実行例
**コマンドを指定してredis-cliを実行する例**

```
root@8f9888c8bb76:/data# redis-cli set mykey test
OK
root@8f9888c8bb76:/data# redis-cli get mykey     
"test"
```

**コマンドを複数回繰り返す例**

```
root@8f9888c8bb76:/data#  redis-cli -r 100 lpush mylist x
```

**コマンドのモニターを行う例**
monitorコマンドを実行することでRedisの操作を監視できます。

```
root@8f9888c8bb76:/data#  redis-cli monitor
OK
1600950437.950595 [0 172.17.0.1:48396] "info"
1600950437.955578 [0 172.17.0.1:48396] "set" "testkey2" "test"
1600950437.961197 [0 172.17.0.1:48396] "get" "testkey2"
```

## node.jsでの使用例
### Node Redis
Node Redisを使用することでnode.jsからredisの操作が行えます。
https://github.com/NodeRedis/node-redis


**インストール方法**

```
npm install --save redis
```

**サンプルコード**
以下のサンプルは文字列値を追加して、それを取得するサンプルです。また、redisの操作についてモニタリングをしています。

```js:test1.js
const redis = require('redis')
const client = redis.createClient(6379, 'localhost')
client.monitor(function(err, res) {
  console.log('client.monitor', err, res);
});
client.on('monitor', function(time, args, rawReply) {
  console.log('on monitor', time, args, rawReply);
});

client.set('testkey2', "test", (err, res)=> {
  console.log(err, res)
  client.get('testkey2', (error, result)=> {
    console.log(error, result);
    client.end(true);
  })
})
```

### IOREDIS
ioredisはredisの機能をフルサポートしており、 Cluster, Sentinel, Streams, Pipeliningなどが使用できます。
またNode RedisではサポートしていないPromisでの記述をサポートしています。

https://github.com/luin/ioredis

2020年9月時点のIOREDISとNode RedisのNPMでのトレンドの比較は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47856/535c51a9-8136-42b0-6558-5a1d452bf406.png)
https://www.npmtrends.com/ioredis-vs-redis

**インストール方法**

```
npm install --save ioredis
```

**サンプルコード**

```js:test2.js
const Redis = require("ioredis");
const redis = new Redis();
let monitor = null;
redis.monitor((err, result)=> {
  monitor = result
  monitor.on('monitor', (time, args)=>{
    console.log('on monitor', time, args)
  })
})
redis.pipeline()
     .set('mykey', 'hogehoge')
     .get('mykey', (err, result)=>{
       console.log('get:', err, result);
     })
     .exec((err, result)=> {
       console.log('exec:', err, result)
       monitor.disconnect()
       redis.disconnect()
     })
```


### redisを使用したsocket.ioのスケールアウトの例
**Socket.ioサーバをスケールアウトする（Redisを使った複数プロセス間でのブロードキャスト）**
https://qiita.com/takehilo/items/8c773d18ec6cfccd6679

下記のモジュールを使用することでredisを使用したsocket.ioのスケールアウトが可能です。
 - [socket.io-redis](https://github.com/socketio/socket.io-redis)
 - [socket.io-adapter](https://github.com/socketio/socket.io-adapte)
 - [sticky-session](https://github.com/indutny/sticky-session)

以下のコードは複数プロセスで動作しているsocket.ioを使用したチャットプログラムのサンプルになります。このコードは[Socket.ioサーバをスケールアウトする（Redisを使った複数プロセス間でのブロードキャスト）](https://qiita.com/takehilo/items/8c773d18ec6cfccd6679)を元に作成しています。


```json:package.json
{
  "name": "socketio",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cluster": "^0.7.7",
    "express": "^4.17.1",
    "socket.io": "^2.3.0",
    "socket.io-redis": "^5.4.0",
    "sticky-session": "^1.1.2"
  }
}
```

```js:server.js
const cluster = require('cluster');
const io = require('socket.io')();
const sticky = require('sticky-session');
const http = require('http');
const redis = require('socket.io-redis');
const express = require("express");
const app = express();
app.use(express.static(__dirname + "/public"));

const server = http.createServer(app);

io.adapter(redis({host: '127.0.0.1', port: 6379}));
io.attach(server);
isWorker = sticky.listen(server, 3000);

if (isWorker) {
  io.on('connection', (socket) => {
    console.log(`worker: ${cluster.worker.id}, connected, id: ${socket.id}`);

    socket.on('message', (user, message) => {
      data = `${message} from ${user}`;
      console.log(data);
      socket.broadcast.emit('message', user, message);
    });

    socket.on('disconnect', () => {
      console.log(`disconnected, id: ${socket.id}`);
    });
  });
}
```

```html:public/index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Sample</title>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <input v-model:value="user"></input>
      <input v-model:value="message"></input>
      <button v-on:click="send">送信</button>
    </div>
    <script src="/socket.io/socket.io.js"></script>
    <script src="/app.js"></script>
  </body>
</html>
```

```js:app.js
const app = new Vue({
  el: '#app',
  data: {
    socket : {},
    user: 'hoge',
    message : 'message'
  },
  created : function() {
    console.log(window.location.origin);
    this.socket = io.connect(window.location.origin);
    console.log(this.socket);
    this.socket.on("message", (user, message) => {
      console.log(user, message);
    })
  },
  methods: {
    send: function() {
      console.log(this.socket);
      console.log(this.user, this.message);
      this.socket.emit("message", this.user, this.message);
    }
  }
})
```