件名通りなのですが、redisにダミーデータを入れ続けるだけのgolangプログラムを書きました!
みんなもつかってね!
…なーんてプログラム使う人居ないと思うので、書いてた時に学んだことを共有しようかなというお話。
■コンソール出力すると極端に遅くなる
golangが早いのはご存知なことだと思うのですが、思ったよりコンソール出力が遅いです。
超大量データを処理する際、毎行ログ出力するのは無駄が多いです。
100行に一回出力とか間引きした方がいいですよ、ということですね。
■Network I/Oはコネクションプーリング等の工夫が必要
処理が早いということで、CPU以外の部分にもボトルネックが来る可能性があります。
redisへのアクセスを毎回切断していたら、あっという間にTCP接続を使い切ってしまいました。
コネクションを使いまわす、を早い段階で考えたほうがいいですね。
■ifでgoroutineを呼び出すときに、ループ数が極端に多いと落ちる
goroutineはループに入る前にスレッド(みたいなもの)を事前に用意するっぽいです。10万ループとかをgoroutineで非同期化すると、「signal: killed」で落ちます。
…ということでどうするかという話なのですが、「
gorc」いうパッケージを使うと回避できます。goroutineの同時並列実行数に制限がかけられるため、リソースを使い切るのが回避できます。すごい。
※追記)sync.Poolとかsync.Cond使うとsyncでもスロットル制御出来る気がします!書いたこと無いけど!
package main
import (
"crypto/md5"
"crypto/rand"
"encoding/hex"
"flag"
"fmt"
"github.com/garyburd/redigo/redis"
"github.com/mr51m0n/gorc"
"math/big"
"runtime"
"strconv"
"time"
)
var (
pool *redis.Pool
host string = "localhost:6379" // redis-server接続情報
key int = 150000 // 投入するKEYの数
val int = 10000 // 文字列の長さ
)
// redis ConnectionPooling
func newPool(server string) *redis.Pool {
return &redis.Pool{
MaxIdle: 3,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", server)
if err != nil {
return nil, err
}
return c, err
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
}
}
// val用にrandomな文字列を生成する
func random(length int) string {
const base = 36
size := big.NewInt(base)
n := make([]byte, length)
for i, _ := range n {
c, _ := rand.Int(rand.Reader, size)
n[i] = strconv.FormatInt(c.Int64(), base)[0]
}
return string(n)
}
// gorc
var gorc0 gorc.Gorc
// メイン処理
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
flag.Parse()
// Timer Set
start := time.Now()
// redis Pooling Start
pool = newPool(host)
// Redis DataInsert
for i := 0; i < key; i++ {
gorc0.Inc()
go setredis(i, val)
gorc0.WaitLow(100)
}
// Log Print
fmt.Print(key)
fmt.Printf("Keys - Set Done.\n")
// Timer Print
end := time.Now()
fmt.Printf("%f秒\n", (end.Sub(start)).Seconds())
}
// stringからmd5から作る
func GetMD5Hash(text string) string {
hash := md5.Sum([]byte(text))
return hex.EncodeToString(hash[:])
}
func init() {
gorc0.Init()
}
// redisにデータを投入するだけの処理
func setredis(k, v int) {
c := pool.Get()
defer c.Close()
defer gorc0.Dec()
vals := random(v)
keys := GetMD5Hash(vals)
c.Do("SET", keys, vals)
// 100処理ごとにログ表示(非同期なので順番が適当)
if k%100 == 0 {
fmt.Print(k)
fmt.Printf("Keys - Set Done.\n")
}
}