Redis介紹
Redis是一個開源的內存數據結構存儲,常用作數據庫、緩存和消息代理。目前它支持的數據結構有諸如string、hash、list、set、zset、bitmap、hyperloglog、geospatial index和stream。Redis內置了復制、Lua腳本、LRU清除、事務和不同級別的磁盤持久性,並通過Redis Sentinel提供高可用性,通過Redis Cluster自動分區。
go-redis庫
安裝
區別於另一個比較常用的Go語言redis client庫:redigo,我們這里采用https://github.com/go-redis/redis連接Redis數據庫並進行操作,因為go-redis支持連接哨兵及集群模式的Redis。
使用以下命令下載並安裝:
go get -u github.com/go-redis/redis
連接
普通連接
// 聲明一個全局的rdb變量
var rdb *redis.Client
// 初始化連接
func initClient() (err error) {
// 通過 redis.NewClient 函數即可創建一個 redis 客戶端, 這個方法接收一個 redis.Options 對象參數, 通過這個參數, 我們可以配置 redis 相關的屬性, 例如 redis 服務器地址, 數據庫名, 數據庫密碼等。
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
// 通過 cient.Ping() 來檢查是否成功連接到了 redis 服務器
_, err = rdb.Ping().Result()
if err != nil {
return err
}
return nil
}
連接Redis哨兵模式
func initClient()(err error){
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "master",
SentinelAddrs: []string{"x.x.x.x:26379", "xx.xx.xx.xx:26379", "xxx.xxx.xxx.xxx:26379"},
})
_, err = rdb.Ping().Result()
if err != nil {
return err
}
return nil
}
連接Redis集群
func initClient()(err error){
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
})
_, err = rdb.Ping().Result()
if err != nil {
return err
}
return nil
}
連接池
go-redis 已經實現了 redis 的連接池管理, 因此我們不需要自己手動管理 redis 的連接。
默認情況下,連接池大小是10, 可以通過 redis.Options 的 PoolSize 屬性, 我們設置了 redis 連接池的大小為5。
func GetRedisClientPool() *Client{
redisdb := NewClient(&Options{
Addr: "127.0.0.1:6379",
Password: "",
DB: 0,
PoolSize: 5,})
pong, err := redisdb.Ping().Result()
if err != nil {
fmt.Println(pong, err)
}
return redisdb
}
// 連接池測試
func connectPoolTest() {
fmt.Println("-----------------------welcome to connect Pool Test-----------------------")
client :=GetRedisClientPool()
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
for j := 0; j < 1000; 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, IdleConns: %d\n", client.PoolStats().TotalConns, client.PoolStats().IdleConns);
}()
}
wg.Wait()
}
基本使用
String 操作
- Set(key, value):給數據庫中名稱為key的string賦予值valueget(key):返回數據庫中名稱為key的string的value
- GetSet(key, value):給名稱為key的string賦予上一次的value
- MGet(key1, key2,…, key N):返回庫中多個string的value
- SetNX(key, value):添加string,名稱為key,值為value
- SetXX(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的值附加valuesubstr(key, start, end):返回名稱為key的string的value的子串
func redisExample() {
err := rdb.Set("score", 100, 0).Err()
if err != nil {
fmt.Printf("set score failed, err:%v\n", err)
return
}
val, err := rdb.Get("score").Result()
if err != nil {
fmt.Printf("get score failed, err:%v\n", err)
return
}
fmt.Println("score", val)
val2, err := rdb.Get("name").Result()
if err == redis.Nil {
fmt.Println("name does not exist")
} else if err != nil {
fmt.Printf("get name failed, err:%v\n", err)
return
} else {
fmt.Println("name", val2)
}
}
func StringDemo() {
fmt.Println("-----------------------welcome to StringDemo-----------------------")
redisClient:=GetRedisClient()
if redisClient ==nil{
fmt.Errorf("StringDemo redisClient is nil")
return
}
name := "張三"
key :="name:zhangsan"
redisClient.Set(key , name,1 * time.Second)
val := redisClient.Get(key)
if val == nil {
fmt.Errorf("StringDemo get error")
}
fmt.Println("name", val)
}
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的頭部
func ListDemo(){
fmt.Println("-----------------------welcome to ListDemo-----------------------")
redisClient:=GetRedisClient()
if redisClient == nil {
fmt.Errorf("ListDemo redisClient is nil")
return
}
articleKey := "article"
result,err:=redisClient.RPush(articleKey, "a","b","c").Result() //
if err!=nil {
fmt.Println(err)
return
}
fmt.Println("result:",result)
result,err = redisClient.LPush(articleKey, "d").Result() //
if err!=nil {
fmt.Println(err)
return
}
fmt.Println("result:",result)
length, err := redisClient.LLen(articleKey).Result()
if err != nil {
fmt.Println("ListDemo LLen is nil")
}
fmt.Println("length: ", length) // 長度
mapOut,err1:=redisClient.LRange(articleKey,0,100).Result()
if err1!=nil {
fmt.Println(err1)
return
}
for inx, item := range mapOut {
fmt.Printf("\n %s:%s", inx, item)
}
}
Hash操作
- 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
func HashDemo() {
fmt.Println("-----------------------welcome to HashDemo-----------------------")
redisClient := GetRedisClient()
if redisClient == nil {
fmt.Errorf("HashDemo redisClient is nil")
return
}
article := Article{18, "測試文章內容22222", "測試文章內容22222測試文章內容22222測試文章內容22222", 10, 0}
articleKey := "article:18"
redisClient.HMSet(articleKey, ToStringDictionary(&article))
mapOut := redisClient.HGetAll(articleKey).Val()
for inx, item := range mapOut {
fmt.Printf("\n %s:%s", inx, item)
}
fmt.Print("\n")
redisClient.HSet(articleKey, "Content", "測試文章內容")
mapOut = redisClient.HGetAll(articleKey).Val()
for inx, item := range mapOut {
fmt.Printf("\n %s:%s", inx, item)
}
fmt.Print("\n")
view, err := redisClient.HIncrBy(articleKey, "Views", 1).Result()
if err != nil {
fmt.Printf("\n HIncrBy error=%s ", err)
} else {
fmt.Printf("\n HIncrBy Views=%d ", view)
}
fmt.Print("\n")
mapOut = redisClient.HGetAll(articleKey).Val()
for inx, item := range mapOut {
fmt.Printf("\n %s:%s", inx, item)
}
fmt.Print("\n")
}
zset示例
func redisExample2() {
zsetKey := "language_rank"
languages := []*redis.Z{
&redis.Z{Score: 90.0, Member: "Golang"},
&redis.Z{Score: 98.0, Member: "Java"},
&redis.Z{Score: 95.0, Member: "Python"},
&redis.Z{Score: 97.0, Member: "JavaScript"},
&redis.Z{Score: 99.0, Member: "C/C++"},
}
// ZADD
num, err := rdb.ZAdd(zsetKey, languages...).Result()
if err != nil {
fmt.Printf("zadd failed, err:%v\n", err)
return
}
fmt.Printf("zadd %d succ.\n", num)
// 把Golang的分數加10
newScore, err := rdb.ZIncrBy(zsetKey, 10.0, "Golang").Result()
if err != nil {
fmt.Printf("zincrby failed, err:%v\n", err)
return
}
fmt.Printf("Golang's score is %f now.\n", newScore)
// 取分數最高的3個
ret, err := rdb.ZRevRangeWithScores(zsetKey, 0, 2).Result()
if err != nil {
fmt.Printf("zrevrange failed, err:%v\n", err)
return
}
for _, z := range ret {
fmt.Println(z.Member, z.Score)
}
// 取95~100分的
op := &redis.ZRangeBy{
Min: "95",
Max: "100",
}
ret, err = rdb.ZRangeByScoreWithScores(zsetKey, op).Result()
if err != nil {
fmt.Printf("zrangebyscore failed, err:%v\n", err)
return
}
for _, z := range ret {
fmt.Println(z.Member, z.Score)
}
}
// 輸出結果如下:
$ ./06redis_demo
zadd 0 succ.
Golang's score is 100.000000 now.
Golang 100
C/C++ 99
Java 98
JavaScript 97
Java 98
C/C++ 99
Golang 100
完整代碼
package main
import (
"fmt"
. "github.com/go-redis/redis"
. "redisDemo/models"
"time"
"sync"
)
func main() {
fmt.Println("-----------------------welcome to redisdemo-----------------------")
//StringDemo()
//ListDemo()
//HashDemo()
connectPoolTest()
}
func StringDemo() {
fmt.Println("-----------------------welcome to StringDemo-----------------------")
redisClient:=GetRedisClient()
if redisClient ==nil{
fmt.Errorf("StringDemo redisClient is nil")
return
}
name := "張三"
key :="name:zhangsan"
redisClient.Set(key , name,1 * time.Second)
val := redisClient.Get(key)
if val == nil {
fmt.Errorf("StringDemo get error")
}
fmt.Println("name", val)
}
func ListDemo(){
fmt.Println("-----------------------welcome to ListDemo-----------------------")
redisClient:=GetRedisClient()
if redisClient == nil {
fmt.Errorf("ListDemo redisClient is nil")
return
}
articleKey := "article"
result,err:=redisClient.RPush(articleKey, "a","b","c").Result() //在名稱為 key 的list尾添加一個值為value的元素
if err!=nil {
fmt.Println(err)
return
}
fmt.Println("result:",result)
result,err = redisClient.LPush(articleKey, "d").Result() //在名稱為 key 的list頭添加一個值為value的元素
if err!=nil {
fmt.Println(err)
return
}
fmt.Println("result:",result)
length, err := redisClient.LLen(articleKey).Result()
if err != nil {
fmt.Println("ListDemo LLen is nil")
}
fmt.Println("length: ", length) // 長度
mapOut,err1:=redisClient.LRange(articleKey,0,100).Result()
if err1!=nil {
fmt.Println(err1)
return
}
for inx, item := range mapOut {
fmt.Printf("\n %s:%s", inx, item)
}
}
func HashDemo() {
fmt.Println("-----------------------welcome to HashDemo-----------------------")
redisClient := GetRedisClient()
if redisClient == nil {
fmt.Errorf("HashDemo redisClient is nil")
return
}
article := Article{18, "測試文章內容22222", "測試文章內容22222測試文章內容22222測試文章內容22222", 10, 0}
articleKey := "article:18"
redisClient.HMSet(articleKey, ToStringDictionary(&article))
mapOut := redisClient.HGetAll(articleKey).Val()
for inx, item := range mapOut {
fmt.Printf("\n %s:%s", inx, item)
}
fmt.Print("\n")
redisClient.HSet(articleKey, "Content", "測試文章內容")
mapOut = redisClient.HGetAll(articleKey).Val()
for inx, item := range mapOut {
fmt.Printf("\n %s:%s", inx, item)
}
fmt.Print("\n")
view, err := redisClient.HIncrBy(articleKey, "Views", 1).Result()
if err != nil {
fmt.Printf("\n HIncrBy error=%s ", err)
} else {
fmt.Printf("\n HIncrBy Views=%d ", view)
}
fmt.Print("\n")
mapOut = redisClient.HGetAll(articleKey).Val()
for inx, item := range mapOut {
fmt.Printf("\n %s:%s", inx, item)
}
fmt.Print("\n")
}
func GetRedisClient() *Client {
redisdb := NewClient(&Options{
Addr: "127.0.0.1:6379",
Password: "", // no password set
DB: 0, // use default DB
})
pong, err := redisdb.Ping().Result()
if err != nil {
fmt.Println(pong, err)
}
return redisdb
}
func GetRedisClientPool() *Client{
redisdb := NewClient(&Options{
Addr: "127.0.0.1:6379",
Password: "",
DB: 0,
PoolSize: 5,})
pong, err := redisdb.Ping().Result()
if err != nil {
fmt.Println(pong, err)
}
return redisdb
}
// 連接池測試
func connectPoolTest() {
fmt.Println("-----------------------welcome to connect Pool Test-----------------------")
client :=GetRedisClientPool()
wg := sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
for j := 0; j < 1000; 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, IdleConns: %d\n", client.PoolStats().TotalConns, client.PoolStats().IdleConns);
}()
}
wg.Wait()
}