Go
Redis
Go言語
redigo

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

始めに

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

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

実践!

環境

  • golang ver1.4.2
  • Redis ver3.2.4
  • EC2

EC2に導入

EC2にRedisをインストールしてみます。

インストール

sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
sudo yum --enablerepo=remi install redis

サーバー起動

$ sudo service redis start

これでサーバーが起動したはず、、、

以下のコマンドで対話式に確認ができる。

$ redis-cli

スクリーンショット 2018-02-08 0.24.34.png

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

$ 127.0.0.1/6379>

と表示される。

上の画像では

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

で"hoge"っていうkeyのvalueをよこせ!って言ってる。

当然、何も記憶させていないからnilが返ってくる。

そこで、

$ 127.0.0.1/6379> set "hoge" "bell"
OK
$ 127.0.0.1:6379> get "hoge"
"bell"

setで"hoge"というkeyに対して"bell"っていうvalueが保存できた。

改めて、get "hoge"でvalueを呼び出すと保存した"bell"が帰ってきた。



これでEC2へのRedisのインストールは完了!

今度はこれをGoで操作していきます

RedisをGo言語で。Redigo。

必要なパッケージ

github.com/garyburd/redigo/redis

これだけ(笑)

go get github.com/garyburd/redigo/redis

redisとの接続

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

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が使用されているはずです。

接続が確立されている場合、errにはnilが返ります。逆に接続が確立されていない場合は、errにnilが返らずにエラー検出の部分でエラーが出ます。

ちなみに、redis.Connはreturnする値の型を指定していてredisとのコネクションを表すcのデータ型を表しています。

SETとGET

接続ができたら実際に値を入れて、取ってくる動作を実装します。

これは、上記のようにコマンドで行ったやり取りとほぼ同様です。

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

まずはSETです、これはRedisにkeyとvalueを格納する動作になります。

見て分かる通り、コネクションcのDo関数を使います。
次にGETです。

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を渡します。

redis.String()でstring型になります。

この2つが使えれば、基本的なRedisの扱いは大丈夫だと思います。

一応、以下サンプルコードです。

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コマンドで操作します。

スクリーンショット 2018-02-08 0.24.34.png

コマンドで

keys *

で全てのkeyを確認することができます。

試しに新しいkeyとvalueをgo-redis.goを実行していれてみます。

go run go-redis.go

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

スクリーンショット 2018-02-08 0.27.41.png

New_KEYがSETされています。

GETしてみると

スクリーンショット 2018-02-08 0.28.27.png

New_VALUEが取り出せています。

ちなみに、Key Value Storeなので値の更新は同じにKeyに対してSETして上書きするだけです。

当然、Keyを重複してRedisに登録することはできません。

疑問

ここで1つ疑問。1つのKEYに対して複数の要素を与えたい時ってどうすればいいのか。

Keyの重複登録ができないってことは・・・諦めるしかないんですかね。

ってことで調べてみました。

これはRedis自体の話になるんですが、格納するデータ構造によって登録するコマンドが違うみたいです。

ValueをList型にして、SETしてみても取り出した時にstring型の1文字1文字が数字に変換されてしまいます。

Redisとしては、1つのKeyにValueを詰めていく感じで登録してほしいみたいです。

1つのKeyにValueを詰め込んでいくコマンドはRPUSHです。

スクリーンショット 2018-02-08 0.29.02.png

このコマンドを実行すると、NewListKeyにValueが詰め込まれていきます。

取り出す時はGETではなく、LRANGEです。その際、Keyと取り出す範囲を指定するみたいです。

スクリーンショット 2018-02-08 0.29.51.png

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

これを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])
}

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

スクリーンショット 2018-02-08 0.30.14.png

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

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

終わりに

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

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

参考

Redisのデータ型

github.com/garyburd/redigo/tree/master/redis