Go Redis

Go语言连接Redis库,常用的库 redisgogo-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
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐