5.6 NOSQLデータベースの操作

NoSQL(Not Only SQL)とは、リレーション型ではないデータベースのことを言います。Web2.0の高まりにしたがって、伝統的なリレーショナルデータベースがWeb2.0ページに使われています。特にとても大きな規模で高度にマルチスレッドなSNS型のWeb2.0の純粋な動的ホームページでは明らかに力不足となっています。多くの解決が難しい問題が暴露され、リレーショナルでないデータベースはその特徴から非常に早く発展してきています。

Go言語は21世紀のC言語として、NOSQLもとてもよくサポートしています。現在流行しているNOSQLには主にredis、mongoDB、CassandraとMembase等があります。これらのデータベースはどれも高性能、マルチスレッドといった特徴があり、現在すでに広くあらゆるアプリケーションの中で使用されています。ここでは主にredisとmongoDBの操作についてご説明します。

redis

redisはkey-valueを保存するシステムです。Memcachedに似ていて、保存されるvalue型はもっと多く、string(文字列)、list(リスト)、set(集合)とzset(順序付きset)を含みます。

現在redisが最もよく使われているのは新浪のマイクロブログプラットフォームでしょう。その次にFacebookに買収された画像フォーラムであるinstagramがあります。その他有名なインターネット企業もそうです。

Goは現在redisのドライバで以下をサポートしています

redigoのドライバを使ってデータベースを操作する方法を見てみましょう:


    package main

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

    var (
        Pool *redis.Pool
    )

    func init() {
        redisHost := ":6379"
        Pool = newPool(redisHost)
        close()
    }

    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
            },
        }
    }

    func close() {
        c := make(chan os.Signal, 1)
        signal.Notify(c, os.Interrupt)
        signal.Notify(c, syscall.SIGTERM)
        signal.Notify(c, syscall.SIGKILL)
        go func() {
            <-c
            Pool.Close()
            os.Exit(0)
        }()
    }

    func Get(key string) ([]byte, error) {

        conn := Pool.Get()
        defer conn.Close()

        var data []byte
        data, err := redis.Bytes(conn.Do("GET", key))
        if err != nil {
            return data, fmt.Errorf("error get key %s: %v", key, err)
        }
        return data, err
    }

    func main() {
        test, err := Get("test")
        fmt.Println(test, err)
    }

現在私がforkした最新のドライバではいくつかのbugが修正されています。現在私自身の短縮ドメイン名サービスのプロジェクトの中で使用されています。(毎日200WぐらいのPV数があります。)

https://github.com/astaxie/goredis

以降では私がforkしたこのredisドライバでどのようにデータの操作を行うかご紹介します。

package main

import (
    "github.com/astaxie/goredis"
    "fmt"
)

func main() {
    var client goredis.Client
    // ポートをredisのデフォルトポートに設定
    client.Addr = "127.0.0.1:6379"

    //文字列操作
    client.Set("a", []byte("hello"))
    val, _ := client.Get("a")
    fmt.Println(string(val))
    client.Del("a")

    //list操作
    vals := []string{"a", "b", "c", "d", "e"}
    for _, v := range vals {
        client.Rpush("l", []byte(v))
    }
    dbvals,_ := client.Lrange("l", 0, 4)
    for i, v := range dbvals {
        println(i,":",string(v))
    }
    client.Del("l")
}

redisの操作が非常に簡単だとお分かりいただけたかと思います。実際のプロジェクトの中で使用していますが、性能も非常に高いのです。clientのコマンドとredisのコマンドは基本的に同じです。ですので元のredisの操作と非常によく似ています。

mongoDB

MongoDBは高性能でオープンソース、モードレスなドキュメント型データベースです。これはリレーショナルデータベースとノンリレーショナルデータベースの間のプロダクトです。ノンリレーショナルデータベースの中では機能が最も豊富で、リレーショナルベースに最もよく似ています。サポートされるデータ形式は非常にルーズで、jsonによく似たbjson形式によってデータを保存します。そのため、比較的複雑なデータを保存することができます。Mongoの最大の特徴は検索言語が非常に強力でその文法がオブジェクト指向の検索文に少し似ていることです。データベースに対してインデックスを設定することもできます。

下の図はmysqlとmongoDBの間の対応関係を示しています。非常に簡単だとわかりますが、mongoDBの性能は非常に良いです。

図5.1 MongoDBとMysqlの操作の対応図

現在GoでサポートされているmongoDBのもっとも良いドライバはmgoです。このドライバは現在もっともオフィシャルのpkgになりそうなものです。

次にどのようにしてGoからmongoDBを操作するのかご説明します:


    package main

    import (
        "fmt"
        "gopkg.in/mgo.v2"
        "gopkg.in/mgo.v2/bson"
        "log"
    )

    type Person struct {
        Name  string
        Phone string
    }

    func main() {
        session, err := mgo.Dial("server1.example.com,server2.example.com")
        if err != nil {
            panic(err)
        }
        defer session.Close()

        // Optional. Switch the session to a monotonic behavior.
        session.SetMode(mgo.Monotonic, true)

        c := session.DB("test").C("people")
        err = c.Insert(&Person{"Ale", "+55 53 8116 9639"},
            &Person{"Cla", "+55 53 8402 8510"})
        if err != nil {
            log.Fatal(err)
        }

        result := Person{}
        err = c.Find(bson.M{"name": "Ale"}).One(&result)
        if err != nil {
            log.Fatal(err)
        }

        fmt.Println("Phone:", result.Phone)
    }

mgoの操作方法とbeedbの操作方法はほとんど似ていることがわかります。どちらもstructに基づいて操作する方法です。これこそがGo Styleです。

results matching ""

    No results matching ""