Go Redis
Go語言連接Redis庫,常用的庫 redisgo
和 go-redis
https://github.com/gomodule/redigo
https://github.com/go-redis/redis
新項目更建議直接使用go-redis,它對集羣和哨兵模式支持更好
gorm
http://gorm.book.jasperxu.com/crud.html#c
redigo 基本使用
# 安裝redigo
go get github.com/gomodule/redigo/redis
連接redis
package main
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
func main() {
conn,err := redis.Dial("tcp","127.0.0.1:6379")
if err != nil{
fmt.Println("connect redis error:",err.Error())
return
}
fmt.Println("connect redis success ...")
defer conn.Close()
}
redigo 操作
conn.Do(redis命令),它的命令與redis命令行一致,只要知道redis如何操作,填入Do中即可。
比如
redis> SET name “sun”
go> conn.Do(“SET”, “name”, “sun”)
獲取到的值,使用redis.String()
轉換一下,如果是其他類型,也使用redis.StringMap
redis.Int64
等
package main
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
func main() {
conn,err := redis.Dial("tcp","127.0.0.1:6379")
if err != nil{
fmt.Println("connect redis error:",err.Error())
return
}
fmt.Println("connect redis success ...")
defer conn.Close()
_, err = conn.Do("SET", "name", "sun")
if err != nil{
fmt.Println("redis set error",err.Error())
return
}
name,err := redis.String(conn.Do("GET", "name"))
if err != nil{
fmt.Println("redis get error",err.Error())
return
} else {
fmt.Println("redis get name",name)
}
}
pipeline
管道批量操作
conn.Send(command1)
conn.Send(command1)
conn.Send(command1)
conn.Flush()
res1,err := conn.Receive()
res2,err := conn.Receive()
res3,err := conn.Receive()
package main
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
func main() {
conn,err := redis.Dial("tcp","127.0.0.1:6379")
if err != nil{
fmt.Println("connect redis error:",err.Error())
return
}
fmt.Println("connect redis success ...")
defer conn.Close()
_, err = conn.Do("SET", "name", "sun")
if err != nil{
fmt.Println("redis set error",err.Error())
return
}
conn.Send("HSET","user","name","sun","age","30")
conn.Send("HSET","user","sex","female")
conn.Send("HGET","user",'age')
conn.Flush()
res1,err := conn.Receive()
fmt.Printf("Receive res:%v\n",res1)
res2,err := conn.Receive()
fmt.Printf("Receive res:%v\n",res2)
res3,err := conn.Receive()
fmt.Printf("Receive res:%v\n",res3)
}
如果檢查命令沒有問題,可能是redis版本過低,請升級版本。
ERR wrong number of arguments for 'hset' command
發佈/訂閱模式
package main
import (
"fmt"
"github.com/gomodule/redigo/redis"
"time"
)
// 訂閱,接收消息
func Subscribe() {
// 連接redis
conn,err := redis.Dial("tcp","127.0.0.1:6379")
if err != nil{
fmt.Println("connect redis error:",err.Error())
return
}
defer conn.Close()
psc := redis.PubSubConn{conn}
psc.Subscribe("channel001") //訂閱channel001頻道
for {
switch v:=psc.Receive().(type) {
case redis.Message:
msg := string(v.Data)
fmt.Println("redis.message",v.Channel,msg)
case redis.Subscription:
fmt.Println("redis.Subscription",v.Channel,v.Kind,v.Count)
case error:
fmt.Println(v)
return
}
}
}
// 發佈者,發送消息
func Push(message string){
conn,_ := redis.Dial("tcp","127.0.0.1:6379")
defer conn.Close()
_,err := conn.Do("PUBLISH","channel001",message)
if err != nil {
fmt.Println("push err",err.Error())
return
}
}
func main() {
go Subscribe()
time.Sleep(time.Second * 2) // 這裏是防止訂閱者啓動比發佈者慢,啓動後已經沒有push發佈了。
go Push("this is a message,one .")
go Push("this is a message,two .")
time.Sleep(time.Second * 3 )
}
事務操作
MULTI 開啓事務
EXEC 執行事務
DISCARD 取消事務
WATCH 監控事務中的變化,一旦有變化則取消事務
c.Send("MULTI") // 開啓事務
c.Send("INCR", "foo")
c.Send("INCR", "bar")
r, err := c.Do("EXEC") // 執行事務
補充:
https://blog.csdn.net/sxj6977380/article/details/80794256
事務開啓和提交,事務開始和取消,提交後的事務沒有取消的操作。
事務提交後失敗的情況有2種,語法檢測沒通過,則會直接取消
語法檢測通過,但是中間過程報錯,比如字符串的key,用hash的操作,那麼後面的的還會正常執行
watch 監聽某個值,在開啓事務之前,提交/取消後,這個監聽也消失了。
使用連接池
package main
import (
"fmt"
"github.com/gomodule/redigo/redis"
"time"
)
var Pool redis.Pool
func init() {
Pool = redis.Pool {
MaxIdle: 16,// 最大空閒連接數
MaxActive: 16,// 最大激活連接數
IdleTimeout: 120, //空間連接時間,超過就會斷開
Dial: func() (redis.Conn, error) {//連接的函數
c,err := redis.Dial("tcp","127.0.0.1:6379",
redis.DialConnectTimeout(30 * time.Millisecond),
redis.DialReadTimeout(50 * time.Millisecond), // 5
redis.DialWriteTimeout(50 * time.Millisecond)) // 5
if err != nil{
fmt.Println(err)
return nil,err
}
return c,nil
},
}
}
func do_something(){
conn := Pool.Get()
defer conn.Close()
res,err := conn.Do("HSET","user","like","ball")
fmt.Println(res,err)
res1,err := redis.String(conn.Do("HGET","user","like"))
fmt.Println(res1,err)
}
func main() {
do_something()
}
數據庫認真/切換/連接檢測
c.Do("AUTH",password)
c.Do("SELECT",dbnum)
c.Do("PING")
go-redis
注意:哨兵和集羣是go-redis的,不是上面的redigo
https://github.com/go-redis/redis
哨兵模式
https://blog.csdn.net/busai2/article/details/81449422
集羣模式
16384個槽,爲什麼?
Redis 應用
ZSET : 榜單,排行榜,定時任務
INCR : 點贊
LIST : 管理後臺發送通知郵件短信
SET :去重
緩存:緩存熱點數據,緩存擊穿/雪崩/穿透
更多應用場景,推薦書籍 <Redis開發與運維> <Redis實戰>
緩存穿透:
查詢的數據 redis沒有,mysql也沒有,導致每次請求都會達到mysql
查詢爲空也短暫緩存 + 布隆過濾器
緩存雪崩
同一時間,大量redis-key過期,導致請求到達mysql
key的過期時間隨機波動性
緩存擊穿
大量請求 正好訪問一個恰巧過期的key
限流,二級緩存
優化技巧
- key 儘量都要有過期時間
- 一級key不要太多
- big key 儘量少
- key 分級 shop:userinfo:1000 shop:userinfo:2000