【Redis】Go言語で高速呼び出しKVS【Redigo】

# 始めに
Go言語のORMが少しずつのびてきているので、次はKVSであるRedisをGo言語で触ってみようと思います。

たまにデータベースからの呼び出しの遅さがボトルネックになることに。
これをKVSの導入によって解消！みたいな感じです。

# 実践！
## 環境
* golang ver1.4.2
* Redis ver3.2.4
* EC2 

## EC2に導入
EC2にRedisをインストールしてみます。<br>

### インストール
```
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
sudo yum --enablerepo=remi install redis
```
## サーバー起動
```
$ sudo service redis start
```

これでサーバーが起動したはず、、、<br>
以下のコマンドで対話式に確認ができる。<br>

```
$ redis-cli
```

<img width="407" alt="スクリーンショット 2018-02-08 0.24.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/bfbd64e5-f058-372d-5969-77b87432523b.png">

ちゃんと起動出来ていると、

```
$ 127.0.0.1/6379>
```

と表示される。<br>
上の画像では<br>

```
$ 127.0.0.1/6379> get "hoge"
<nil>
```

で"hoge"っていうkeyのvalueをよこせ！って言ってる。<br>
当然、何も記憶させていないからnilが返ってくる。<br>
そこで、

```
$ 127.0.0.1/6379> set "hoge" "bell"
OK
$ 127.0.0.1:6379> get "hoge"
"bell"
```
setで"hoge"というkeyに対して"bell"っていうvalueが保存できた。<br>
改めて、get "hoge"でvalueを呼び出すと保存した"bell"が帰ってきた。<br>
<br>
これでEC2へのRedisのインストールは完了！<br>
今度はこれをGoで操作していきます<br>

# RedisをGo言語で。Redigo。

## 必要なパッケージ
github.com/garyburd/redigo/redis<br>
これだけ(笑)<br>

```go get github.com/garyburd/redigo/redis```

## redisとの接続
まずはEC2にインストールしたRedisと接続を確立します。

```Golang
func redis_connection() redis.Conn {
  const IP_PORT = "52.###.55.###:6379"

  //redisに接続
  c, err := redis.Dial("tcp", IP_PORT)
  if err != nil {
    panic(err)
  }
  return c
}
```
接続には以下の要素が必要になります。

* プロトコル：tcp
* IPアドレス：52.###.55.###
* ポート番号：6379(デフォルト設定)

IPアドレスはredisがインストールされているサーバーのIPアドレスで、ポート番号はデフォルト設定されてるものでは6379が使用されているはずです。<br>
接続が確立されている場合、errにはnilが返ります。逆に接続が確立されていない場合は、errにnilが返らずにエラー検出の部分でエラーが出ます。<br>
ちなみに、redis.Connはreturnする値の型を指定していてredisとのコネクションを表すcのデータ型を表しています。

## SETとGET
接続ができたら実際に値を入れて、取ってくる動作を実装します。<br>
これは、上記のようにコマンドで行ったやり取りとほぼ同様です。<br>

```Golang
func redis_set(key string, value string, c redis.Conn){
  c.Do("SET", key, value)
}
```

まずはSETです、これはRedisにkeyとvalueを格納する動作になります。<br>
見て分かる通り、コネクションcのDo関数を使います。
次にGETです。

```Golang
func redis_get(key string, c redis.Conn) string{
  s, err := redis.String(c.Do("GET", key))
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
  return s
}
```

第一引数に"GET"を渡して、取り出したいvalueのkeyを渡します。<br>
redis.String()でstring型になります。

この２つが使えれば、基本的なRedisの扱いは大丈夫だと思います。<br>
一応、以下サンプルコードです。<br>

```Golang:go-redis.go
package main

import (
  "github.com/garyburd/redigo/redis"
  "fmt"
  "os"
)

func redis_set(key string, value string, c redis.Conn){
  c.Do("SET", key, value)
}

func redis_get(key string, c redis.Conn) string{
  s, err := redis.String(c.Do("GET", key))
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
  return s
}

func redis_connection() redis.Conn {
  const IP_PORT = "52.###.55.###:6379"

  //redisに接続
  c, err := redis.Dial("tcp", IP_PORT)
  if err != nil {
    panic(err)
  }
  return c
}

func main() {
  c := redis_connection()
  defer c.Close()

  var key = "KEY"
  var val = "VALUE"
  redis_set(key, val, c)
  s := redis_get(key, c)
  fmt.Println(s)
}
```

## Redisで確認
Redisに登録されているkeyやvalueを確認したい時ありますよね！そんな時は、Redis-cliコマンドで操作します。<br>
<img width="407" alt="スクリーンショット 2018-02-08 0.24.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/1271f55a-ba2d-8d72-92e4-b3ff2ed1f32f.png">

コマンドで

```keys *```

で全てのkeyを確認することができます。<br>
試しに新しいkeyとvalueをgo-redis.goを実行していれてみます。<br>

```go run go-redis.go```

実行後にもう一度、key確認をしてみると

<img width="709" alt="スクリーンショット 2018-02-08 0.27.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/66c73d07-92d8-dd09-a7b8-9a48d8a90b1a.png">


New_KEYがSETされています。<br>
GETしてみると<br>

<img width="716" alt="スクリーンショット 2018-02-08 0.28.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/e218b052-0d7c-f640-68dc-9b2e6792e7d8.png">

New_VALUEが取り出せています。<br>

ちなみに、Key Value Storeなので値の更新は同じにKeyに対してSETして上書きするだけです。<br>
当然、Keyを重複してRedisに登録することはできません。<br>

## 疑問
ここで1つ疑問。1つのKEYに対して複数の要素を与えたい時ってどうすればいいのか。<br>
Keyの重複登録ができないってことは・・・諦めるしかないんですかね。<br>
ってことで調べてみました。<br>
これはRedis自体の話になるんですが、格納するデータ構造によって登録するコマンドが違うみたいです。<br>
ValueをList型にして、SETしてみても取り出した時にstring型の1文字1文字が数字に変換されてしまいます。<br>
Redisとしては、1つのKeyにValueを詰めていく感じで登録してほしいみたいです。<br>
1つのKeyにValueを詰め込んでいくコマンドはRPUSHです。<br>


<img width="718" alt="スクリーンショット 2018-02-08 0.29.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/4104aa98-e70b-5df0-66ef-80a4f58d0c83.png">

このコマンドを実行すると、NewListKeyにValueが詰め込まれていきます。<br>
取り出す時はGETではなく、LRANGEです。その際、Keyと取り出す範囲を指定するみたいです。<br>

<img width="595" alt="スクリーンショット 2018-02-08 0.29.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/51aa9bc2-8ff7-8aef-2e77-bb4848275267.png">

第三引数が取り出し先頭のインデックス、第四引数が末尾のインデックスになります。-1を指定すると全ての値を取り出します。<br>

これをGolangで書くと以下になります。

```Golang:go-redis-list.go
package main

import (
  "github.com/garyburd/redigo/redis"
  "fmt"
  "os"
  _ "reflect"
)

func redisSet(key string, value string, c redis.Conn){
  c.Do("SET", key, value)
}

func redisSetList(key string, value []string, c redis.Conn){
  for _ , v := range value {
    fmt.Println(v)
    c.Do("RPUSH", key, v)
  }
}

func redisGet(key string, c redis.Conn) string{
  s, err := redis.String(c.Do("GET", key))
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
  return s
}

func redisGetList(key string, c redis.Conn) []string{
  s, err := redis.Strings(c.Do("LRANGE", key, 0, -1))
  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
  return s
}
func redisConnection() redis.Conn {
  const IP_PORT = "52.###.55.###:6379"

  //redisに接続
  c, err := redis.Dial("tcp", IP_PORT)
  if err != nil {
    panic(err)
  }
  return c
}

func main() {
  c := redisConnection()
  defer c.Close()

  var key = "New_KEY"
  var val = "Re_VALUE"
  redisSet(key, val, c)
  s := redisGet(key, c)
  fmt.Println(s)

  key = "List"
  var vallist = []string{"redis", "cash", "kvs"}
  redisSetList(key, vallist, c)
  sl := redisGetList(key, c)
  fmt.Println(sl)
  fmt.Println(sl[0])
  fmt.Println(sl[1])
  fmt.Println(sl[2])
}
```

実行結果は以下になります。<br>

<img width="592" alt="スクリーンショット 2018-02-08 0.30.14.png" src="https://qiita-image-store.s3.amazonaws.com/0/148317/0e65953c-e82b-9d2f-7823-43fdc6242b9e.png">


これで1つのKeyに複数の要素を詰め込んで取り出すことができました！<br>

以上で実践編はおしまいです。

# 終わりに
今回は最低限GETとSETができれば値の高速な呼び出しが可能となります。
今度はこれをアプリケーションに組み込んで、威力を確認してみたいと思います。


Go触っている方の少しでも参考になれば幸いです。



# 参考
[Redisのデータ型](http://blog.sojiro.me/blog/2014/07/27/rediss-data-type-and-usage/)<br>
[github.com/garyburd/redigo/tree/master/redis](https://github.com/garyburd/redigo/tree/master/redis)<br>