Edited at

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

More than 1 year has passed since last update.


始めに

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