使用Goland操做Redis詳解

String 操做

redis 的 String 操做有:golang

set(key, value):給數據庫中名稱爲key的string賦予值value
get(key):返回數據庫中名稱爲key的string的value
getset(key, value):給名稱爲key的string賦予上一次的value
mget(key1, key2,…, key N):返回庫中多個string的value
setnx(key, value):添加string,名稱爲key,值爲value
setex(key, time, value):向庫中添加string,設定過時時間time
mset(key N, value N):批量設置多個string的值
msetnx(key N, value N):若是全部名稱爲key i的string都不存在
incr(key):名稱爲key的string增1操做
incrby(key, integer):名稱爲key的string增長integer
decr(key):名稱爲key的string減1操做
decrby(key, integer):名稱爲key的string減小integer
append(key, value):名稱爲key的string的值附加value
substr(key, start, end):返回名稱爲key的string的value的子串

在 go-redis 中, 咱們能夠直接找到對應的操做方法, 直接上代碼:redis

// String 操做
func stringOperation(client *redis.Client) {
    // 第三個參數是過時時間, 若是是0, 則表示沒有過時時間.
    err := client.Set("name", "xys", 0).Err()
    if err != nil {
        panic(err)
    }

    val, err := client.Get("name").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("name", val)


    // 這裏設置過時時間.
    err = client.Set("age", "20", 1 * time.Second).Err()
    if err != nil {
        panic(err)
    }

    client.Incr("age") // 自增
    client.Incr("age") // 自增
    client.Decr("age") // 自減

    val, err = client.Get("age").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("age", val) // age 的值爲21

    // 由於 key "age" 的過時時間是一秒鐘, 所以當一秒後, 此 key 會自動被刪除了.
    time.Sleep(1 * time.Second)
    val, err = client.Get("age").Result()
    if err != nil {
        // 由於 key "age" 已通過期了, 所以會有一個 redis: nil 的錯誤.
        fmt.Printf("error: %v\n", err)
    }
    fmt.Println("age", val)
}

list 操做

redis 的 list 操做有:數據庫

rpush(key, value):在名稱爲key的list尾添加一個值爲value的元素
lpush(key, value):在名稱爲key的list頭添加一個值爲value的 元素
llen(key):返回名稱爲key的list的長度
lrange(key, start, end):返回名稱爲key的list中start至end之間的元素
ltrim(key, start, end):截取名稱爲key的list
lindex(key, index):返回名稱爲key的list中index位置的元素
lset(key, index, value):給名稱爲key的list中index位置的元素賦值
lrem(key, count, value):刪除count個key的list中值爲value的元素
lpop(key):返回並刪除名稱爲key的list中的首元素
rpop(key):返回並刪除名稱爲key的list中的尾元素
blpop(key1, key2,… key N, timeout):lpop命令的block版本。
brpop(key1, key2,… key N, timeout):rpop的block版本。
rpoplpush(srckey, dstkey):返回並刪除名稱爲srckey的list的尾元素,並將該元素添加到名稱爲dstkey的list的頭部

一樣地, 在 go-redis 中也能夠找到對應的方法, 下面是一個簡單的示例:segmentfault

// list 操做
func listOperation(client *redis.Client) {
    client.RPush("fruit", "apple") //在名稱爲 fruit 的list尾添加一個值爲value的元素
    client.LPush("fruit", "banana") //在名稱爲 fruit 的list頭添加一個值爲value的 元素
    length, err := client.LLen("fruit").Result() //返回名稱爲 fruit 的list的長度
    if err != nil {
        panic(err)
    }
    fmt.Println("length: ", length) // 長度爲2

    value, err := client.LPop("fruit").Result() //返回並刪除名稱爲 fruit 的list中的首元素
    if err != nil {
        panic(err)
    }
    fmt.Println("fruit: ", value)

    value, err = client.RPop("fruit").Result() // 返回並刪除名稱爲 fruit 的list中的尾元素
    if err != nil {
        panic(err)
    }
    fmt.Println("fruit: ", value)
}

set 操做

redis 的 set 操做:app

sadd(key, member):向名稱爲key的set中添加元素member
srem(key, member) :刪除名稱爲key的set中的元素member
spop(key) :隨機返回並刪除名稱爲key的set中一個元素
smove(srckey, dstkey, member) :移到集合元素
scard(key) :返回名稱爲key的set的基數
sismember(key, member) :member是不是名稱爲key的set的元素
sinter(key1, key2,…key N) :求交集
sinterstore(dstkey, (keys)) :求交集並將交集保存到dstkey的集合
sunion(key1, (keys)) :求並集
sunionstore(dstkey, (keys)) :求並集並將並集保存到dstkey的集合
sdiff(key1, (keys)) :求差集
sdiffstore(dstkey, (keys)) :求差集並將差集保存到dstkey的集合
smembers(key) :返回名稱爲key的set的全部元素
srandmember(key) :隨機返回名稱爲key的set的一個元素

接下來是 go-redis 的 set 操做:ui

// set 操做
func setOperation(client *redis.Client) {
    client.SAdd("blacklist", "Obama") // 向 blacklist 中添加元素
    client.SAdd("blacklist", "Hillary") // 再次添加
    client.SAdd("blacklist", "the Elder") // 添加新元素

    client.SAdd("whitelist", "the Elder") // 向 whitelist 添加元素

    // 判斷元素是否在集合中
    isMember, err := client.SIsMember("blacklist", "Bush").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("Is Bush in blacklist: ", isMember)


    // 求交集, 即既在黑名單中, 又在白名單中的元素
    names, err := client.SInter("blacklist", "whitelist").Result()
    if err != nil {
        panic(err)
    }
    // 獲取到的元素是 "the Elder"
    fmt.Println("Inter result: ", names)


    // 獲取指定集合的全部元素
    all, err := client.SMembers("blacklist").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("All member: ", all)
}

hash 操做

redis 的 hash 操做:code

hset(key, field, value):向名稱爲key的hash中添加元素field
hget(key, field):返回名稱爲key的hash中field對應的value
hmget(key, (fields)):返回名稱爲key的hash中field i對應的value
hmset(key, (fields)):向名稱爲key的hash中添加元素field 
hincrby(key, field, integer):將名稱爲key的hash中field的value增長integer
hexists(key, field):名稱爲key的hash中是否存在鍵爲field的域
hdel(key, field):刪除名稱爲key的hash中鍵爲field的域
hlen(key):返回名稱爲key的hash中元素個數
hkeys(key):返回名稱爲key的hash中全部鍵
hvals(key):返回名稱爲key的hash中全部鍵對應的value
hgetall(key):返回名稱爲key的hash中全部的鍵(field)及其對應的value

go-redis 中的 hash 操做:rem

// hash 操做
func hashOperation(client *redis.Client) {
    client.HSet("user_xys", "name", "xys"); // 向名稱爲 user_xys 的 hash 中添加元素 name
    client.HSet("user_xys", "age", "18"); // 向名稱爲 user_xys 的 hash 中添加元素 age

    // 批量地向名稱爲 user_test 的 hash 中添加元素 name 和 age
    client.HMSet("user_test", map[string]string{"name": "test", "age":"20"})
    // 批量獲取名爲 user_test 的 hash 中的指定字段的值.
    fields, err := client.HMGet("user_test", "name", "age").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("fields in user_test: ", fields)


    // 獲取名爲 user_xys 的 hash 中的字段個數
    length, err := client.HLen("user_xys").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("field count in user_xys: ", length) // 字段個數爲2

    // 刪除名爲 user_test 的 age 字段
    client.HDel("user_test", "age")
    age, err := client.HGet("user_test", "age").Result()
    if err != nil {
        fmt.Printf("Get user_test age error: %v\n", err)
    } else {
        fmt.Println("user_test age is: ", age) // 字段個數爲2
    }
}

關於鏈接池

redis.v4 包實現了 redis 的鏈接池管理, 所以咱們就不須要本身手動管理 redis 的鏈接了.
默認狀況下, redis.v4 的 redis 鏈接池大小是10, 不過咱們能夠在初始化 redis 客戶端時自行設置鏈接池的大小, 例如:get

client := redis.NewClient(&redis.Options{
    Addr:     "localhost:6379",
    Password: "",
    DB:       0,
    PoolSize: 5,
})

經過 redis.Options 的 PoolSize 屬性, 咱們設置了 redis 鏈接池的大小爲5.
那麼接下來咱們來看一下這個設置有什麼效果吧:string

// redis.v4 的鏈接池管理
func connectPool(client *redis.Client) {
    wg := sync.WaitGroup{}
    wg.Add(10)

    for i := 0; i < 10; i++ {
        go func() {
            defer wg.Done()

            for j := 0; j < 100; j++ {
                client.Set(fmt.Sprintf("name%d", j), fmt.Sprintf("xys%d", j), 0).Err()
                client.Get(fmt.Sprintf("name%d", j)).Result()
            }

            fmt.Printf("PoolStats, TotalConns: %d, FreeConns: %d\n", client.PoolStats().TotalConns, client.PoolStats().FreeConns);
        }()
    }

    wg.Wait()
}

上面的例子啓動了10個 routine 來不斷向 redis 讀寫數據, 而後咱們經過 client.PoolStats() 獲取鏈接池的信息. 運行這個例子, 輸出以下:

PoolStats, TotalConns: 5, FreeConns: 1
PoolStats, TotalConns: 5, FreeConns: 1
PoolStats, TotalConns: 5, FreeConns: 1
PoolStats, TotalConns: 5, FreeConns: 1
PoolStats, TotalConns: 5, FreeConns: 1
PoolStats, TotalConns: 5, FreeConns: 2
PoolStats, TotalConns: 5, FreeConns: 2
PoolStats, TotalConns: 5, FreeConns: 3
PoolStats, TotalConns: 5, FreeConns: 4
PoolStats, TotalConns: 5, FreeConns: 5

經過輸出能夠看到, 此時最大的鏈接池數量確實是 5 了, 而且一開始時, 由於 coroutine 的數量大於5, 會形成 redis 鏈接不足的狀況(反映在 FreeConns 上就是前幾回的輸出 FreeConns 一直是1), 當某個 coroutine 結束後, 會釋放此 redis 鏈接, 所以 FreeConns 會增長.

完整示例

//
// author xiongyongshun
// project go_redis
// version 1.0
// created 16/10/6 03:49
//
package main

import (
    "fmt"
    "gopkg.in/redis.v4"
    "time"
    "sync"
)

func main() {
    client := createClient()
    defer client.Close()

    stringOperation(client)
    listOperation(client)
    setOperation(client)
    hashOperation(client)

    connectPool(client)

}

// 建立 redis 客戶端
func createClient() *redis.Client {
    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "",
        DB:       0,
        PoolSize: 5,
    })

    pong, err := client.Ping().Result()
    fmt.Println(pong, err)

    return client
}


// String 操做
func stringOperation(client *redis.Client) {
    // 第三個參數是過時時間, 若是是0, 則表示沒有過時時間.
    err := client.Set("name", "xys", 0).Err()
    if err != nil {
        panic(err)
    }

    val, err := client.Get("name").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("name", val)


    // 這裏設置過時時間.
    err = client.Set("age", "20", 1 * time.Second).Err()
    if err != nil {
        panic(err)
    }

    client.Incr("age") // 自增
    client.Incr("age") // 自增
    client.Decr("age") // 自減

    val, err = client.Get("age").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("age", val) // age 的值爲21

    // 由於 key "age" 的過時時間是一秒鐘, 所以當一秒後, 此 key 會自動被刪除了.
    time.Sleep(1 * time.Second)
    val, err = client.Get("age").Result()
    if err != nil {
        // 由於 key "age" 已通過期了, 所以會有一個 redis: nil 的錯誤.
        fmt.Printf("error: %v\n", err)
    }
    fmt.Println("age", val)
}

// list 操做
func listOperation(client *redis.Client) {
    client.RPush("fruit", "apple") //在名稱爲 fruit 的list尾添加一個值爲value的元素
    client.LPush("fruit", "banana") //在名稱爲 fruit 的list頭添加一個值爲value的 元素
    length, err := client.LLen("fruit").Result() //返回名稱爲 fruit 的list的長度
    if err != nil {
        panic(err)
    }
    fmt.Println("length: ", length) // 長度爲2

    value, err := client.LPop("fruit").Result() //返回並刪除名稱爲 fruit 的list中的首元素
    if err != nil {
        panic(err)
    }
    fmt.Println("fruit: ", value)

    value, err = client.RPop("fruit").Result() // 返回並刪除名稱爲 fruit 的list中的尾元素
    if err != nil {
        panic(err)
    }
    fmt.Println("fruit: ", value)
}

// set 操做
func setOperation(client *redis.Client) {
    client.SAdd("blacklist", "Obama") // 向 blacklist 中添加元素
    client.SAdd("blacklist", "Hillary") // 再次添加
    client.SAdd("blacklist", "the Elder") // 添加新元素

    client.SAdd("whitelist", "the Elder") // 向 whitelist 添加元素

    // 判斷元素是否在集合中
    isMember, err := client.SIsMember("blacklist", "Bush").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("Is Bush in blacklist: ", isMember)


    // 求交集, 即既在黑名單中, 又在白名單中的元素
    names, err := client.SInter("blacklist", "whitelist").Result()
    if err != nil {
        panic(err)
    }
    // 獲取到的元素是 "the Elder"
    fmt.Println("Inter result: ", names)


    // 獲取指定集合的全部元素
    all, err := client.SMembers("blacklist").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("All member: ", all)
}


// hash 操做
func hashOperation(client *redis.Client) {
    client.HSet("user_xys", "name", "xys"); // 向名稱爲 user_xys 的 hash 中添加元素 name
    client.HSet("user_xys", "age", "18"); // 向名稱爲 user_xys 的 hash 中添加元素 age

    // 批量地向名稱爲 user_test 的 hash 中添加元素 name 和 age
    client.HMSet("user_test", map[string]string{"name": "test", "age":"20"})
    // 批量獲取名爲 user_test 的 hash 中的指定字段的值.
    fields, err := client.HMGet("user_test", "name", "age").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("fields in user_test: ", fields)


    // 獲取名爲 user_xys 的 hash 中的字段個數
    length, err := client.HLen("user_xys").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("field count in user_xys: ", length) // 字段個數爲2

    // 刪除名爲 user_test 的 age 字段
    client.HDel("user_test", "age")
    age, err := client.HGet("user_test", "age").Result()
    if err != nil {
        fmt.Printf("Get user_test age error: %v\n", err)
    } else {
        fmt.Println("user_test age is: ", age) // 字段個數爲2
    }
}

// redis.v4 的鏈接池管理
func connectPool(client *redis.Client) {
    wg := sync.WaitGroup{}
    wg.Add(10)

    for i := 0; i < 10; i++ {
        go func() {
            defer wg.Done()

            for j := 0; j < 100; j++ {
                client.Set(fmt.Sprintf("name%d", j), fmt.Sprintf("xys%d", j), 0).Err()
                client.Get(fmt.Sprintf("name%d", j)).Result()
            }

            fmt.Printf("PoolStats, TotalConns: %d, FreeConns: %d\n", client.PoolStats().TotalConns, client.PoolStats().FreeConns);
        }()
    }

    wg.Wait()
}
 

本文來自:Segmentfault

感謝做者:永順

相關文章
相關標籤/搜索