Golang Redis常用操作&结构体等缓存(redigo)
一、Redis简介1. Redis是什么?Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据类型字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset)、支持网络、基于内存、可选持久性的键值对存储数据库。2. 为什么要用Redis?解决应用服务器的cpu和内存压力\减少io的读操作,减
·
一、Redis简介
1. Redis是什么?
Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据类型
字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset)
、支持网络、基于内存、可选持久性的键值对存储数据库。
2. 为什么要用Redis?
- 解决应用服务器的cpu和内存压力\
- 减少io的读操作,减轻io的压力\
- 关系型数据库的扩展性不强,难以改变表结构。
通俗点的意思,就是因为redis直接作用于缓存,比关系型的数据库快得多,大多数时候是配合关系型数据库使用,避免访问频率高的数据反复作用于数据库,导致数据库的负载过高,减少了I/O操作
3.使用场景
- 热点数据缓存(本章介绍)
- 计数器
- 排行榜
- 分布式锁
- …
4. Redis作缓存怎么用呢?
访问数据库之前,先去Redis查找有无结果,有就直接返回结果,否则再去数据库中查找,查找到之后在redis做缓存并返回结果。
二、 Golang Redis常用操作
推荐一个网址 redigo API文档
1. 第三方库
go get -u github.com/gomodule/redigo/redis
2. 连接redis
采用的是连接池的方式
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/gomodule/redigo/redis"
"time"
)
var rds redis.Conn
func RedisPollInit() *redis.Pool {
return &redis.Pool{
MaxIdle: 5, //最大空闲数
MaxActive: 0, //最大连接数,0不设上
Wait: true,
IdleTimeout: time.Duration(1) * time.Second, //空闲等待时间
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", "127.0.0.1:6379") //redis IP地址
if err != nil {
fmt.Println(err)
return nil, err
}
redis.DialDatabase(0)
return c, err
},
}
}
func RedisInit() {
rds = RedisPollInit().Get()
}
func RedisClose() {
_ = rds.Close()
}
3. Get&Set
func main(){
RedisInit()
defer RedisClose()
var err error
_, err = rds.Do("set", "name", "abc") //redis set命令
if err != nil {
fmt.Println(err)
return
}
res, err := rds.Do("Get", "name") //redis get命令
if err != nil {
fmt.Println(err)
return
}
fmt.Println("res:", res)
res1, err := redis.String(rds.Do("Get", "name")) //redis get命令
if err != nil {
fmt.Println(err)
return
}
fmt.Println("res1:",res1)
res2, err := redis.Int(rds.Do("Get", "name")) //redis get命令
if err != nil {
fmt.Println(err)
return
}
fmt.Println("res2:",res2)
}
得到结果->
res: [97 98 99]
res1: abc
strconv.ParseInt: parsing "abc": invalid syntax
解析:
Do(commandName string, args ...interface{}) (reply interface{}, err error)
func String(reply interface{}, err error) (string, error)
func Int(reply interface{}, err error) (int, error)
4.过期设置
func main(){
RedisInit()
defer RedisClose()
var err error
_, err = rds.Do("set", "name", "abc", "EX", 5) //redis set命令
if err != nil {
fmt.Println(err)
return
}
res, err := redis.String(rds.Do("Get", "name")) //redis get命令
if err != nil {
fmt.Println(err)
return
}
fmt.Println("res:", res)
time.Sleep(time.Second * 6)
res, err = redis.String(rds.Do("Get", "name")) //redis get命令
if err != nil {
fmt.Println(err)
return
}
fmt.Println("res:", res)
}
得到结果->
res: abc
redigo: nil returned
解析:
Do(commandName string, args ...interface{}) (reply interface{}, err error)
5.分布式锁
func main(){
RedisInit()
defer RedisClose()
var err error
_, err = rds.Do("set", "name1", "abc", "NX", "EX", 5) //redis set命令
if err != nil {
fmt.Println(err)
return
}
_, err = rds.Do("set", "name1", "叶叶子", "NX", "EX", 5) //redis set命令
if err != nil {
fmt.Println(err)
return
}
res, err := redis.String(rds.Do("Get", "name1")) //redis get命令
if err != nil {
fmt.Println(err)
return
}
fmt.Println("res:", res)
time.Sleep(time.Second * 6)
_, err = rds.Do("set", "name1", "叶叶子", "NX", "EX", 5) //redis set命令
if err != nil {
fmt.Println(err)
return
}
res, err = redis.String(rds.Do("Get", "name1")) //redis get命令
if err != nil {
fmt.Println(err)
return
}
fmt.Println("res:", res)
}
得到结果->
res: abc
res: 叶叶子
解析:
-
rds.Do("set", "name1", "abc", "NX", "EX", 5)
执行命令函数,NX
命令:在指定的 key 不存在时,为 key 设置指定的值,否则跳过,可以看到过期之后才能继续设置值。
Do(commandName string, args ...interface{}) (reply interface{}, err error)
6.结构体切片map等等复杂类型缓存处理
type User struct {
Name string
Age int
}
func main() {
RedisInit()
defer RedisClose()
var err error
var user = make([]User, 0)
user = []User{
{Name: "叶叶子", Age: 18},
{Name: "abc", Age: 1},
}
data, err := json.Marshal(user)
_, err = rds.Do("set", "user", data , "EX", 5) //redis set命令
if err != nil {
fmt.Println(err)
return
}
var res = make([]User, 0)
r, err := redis.Bytes(rds.Do("get", "user"))
if len(r) > 0 {
err = json.Unmarshal(r, &res)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("使用redis", res)
}
}
得到结果->
使用redis [{叶叶子 18} {abc 1}]
解析:
-
使用
"encoding/json"
库json.Marshal
,把数据转序列化成json数据存入redis
,取出key值的时候使用redis.Bytes()
解析之后再用json.Unmarshal
反序列化得到结果。
三、Redis风险&解决办法
1、缓存雪崩
- 场景
假设缓存上限最多1秒应答100个请求,但是高峰期每秒200个请求,缓存出现意外,导致请求全部作用到数据库,导致数据库挂掉。
- 解决办法
- redis使用高可用的部署,主从、哨兵、集群的模式;
- 后端限流降级处理;
- redis持久化,可快速恢复。
2、缓存穿透
- 场景
假设缓存上限最多1秒应答100个请求,恶意有人1秒发出100个缓存和数据库都查不到的请求,导致每次都回去查数据库。
- 解决办法
从数据库中只要没查到,就写一个空值到缓存里。
2、缓存击穿
- 场景
热点key时效是,大量请求直接作用到数据库。
- 解决办法
热点数据不过期。
更多推荐
已为社区贡献1条内容
所有评论(0)