首页>>后端>>Golang->Golang Redis常用操作&结构体等缓存(redigo)

Golang Redis常用操作&结构体等缓存(redigo)

时间:2023-11-29 本站 点击:0

一、Redis简介

1. Redis是什么?

Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据类型字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合类型(zset)、支持网络、基于内存、可选持久性的键值对存储数据库。

2. 为什么要用Redis?

1.解决应用服务器的cpu和内存压力 2.减少io的读操作,减轻io的压力 3. 关系型数据库的扩展性不强,难以改变表结构。

通俗点的意思,就是因为Redis直接作用于缓存,比关系型的数据库快得多,大多数时候是配合关系型数据库使用,避免访问频率高的数据反复作用于数据库,导致数据库的负载过高,减少了I/O操作

3.使用场景

热点数据缓存(本章介绍)

计数器

排行榜

分布式锁

......

4. Redis作缓存怎么用呢?

访问数据库之前,先去Redis查找有无结果,有就直接返回结果,否则再去数据库中查找,查找到之后在redis做缓存并返回结果。

二、 Golang Redis常用操作

推荐一个网址 redigo API文档

1. 第三方库

go get -u github.com/gomodule/redigo/redis

2. 连接redis

采用的是连接池的方式

packagemainimport("fmt"_"github.com/go-sql-driver/mysql""github.com/gomodule/redigo/redis""time")varrdsredis.ConnfuncRedisPollInit()*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")//redisIP地址iferr!=nil{fmt.Println(err)returnnil,err}redis.DialDatabase(0)returnc,err},}}funcRedisInit(){rds=RedisPollInit().Get()}funcRedisClose(){_=rds.Close()}

3. Get&Set

funcmain(){RedisInit()deferRedisClose()varerrerror_,err=rds.Do("set","name","abc")//redisset命令iferr!=nil{fmt.Println(err)return}res,err:=rds.Do("Get","name")//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res:",res)res1,err:=redis.String(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res1:",res1)res2,err:=redis.Int(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res2:",res2)}
得到结果->
res:[979899]res1:abcstrconv.ParseInt:parsing"abc":invalidsyntax
解析:
rds.Do() 执行命令函数,返回的是空接口任意类型
Do(commandNamestring,args...interface{})(replyinterface{},errerror)
redis.String() 把GET返回的空接口解析成string类型
funcString(replyinterface{},errerror)(string,error)
redis.Int() 把GET返回的空接口解析成int类型,因为"abc"无法转换成int类型,所以报错
funcInt(replyinterface{},errerror)(int,error)

4.过期设置

funcmain(){RedisInit()deferRedisClose()varerrerror_,err=rds.Do("set","name","abc","EX",5)//redisset命令iferr!=nil{fmt.Println(err)return}res,err:=redis.String(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res:",res)time.Sleep(time.Second*6)res,err=redis.String(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res:",res)}
得到结果->
res:abcredigo:nilreturned
解析:
rds.Do("set", "name", "abc", "EX", 5) 执行命令函数,返回的是空接口任意类型,加入了EX过期命令和过期时间5s
Do(commandNamestring,args...interface{})(replyinterface{},errerror)

5.分布式锁

funcmain(){RedisInit()deferRedisClose()varerrerror_,err=rds.Do("set","name1","abc","NX","EX",5)//redisset命令iferr!=nil{fmt.Println(err)return}_,err=rds.Do("set","name1","叶叶子","NX","EX",5)//redisset命令iferr!=nil{fmt.Println(err)return}res,err:=redis.String(rds.Do("Get","name1"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res:",res)time.Sleep(time.Second*6)_,err=rds.Do("set","name1","叶叶子","NX","EX",5)//redisset命令iferr!=nil{fmt.Println(err)return}res,err=redis.String(rds.Do("Get","name1"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res:",res)}
得到结果->
funcmain(){RedisInit()deferRedisClose()varerrerror_,err=rds.Do("set","name","abc")//redisset命令iferr!=nil{fmt.Println(err)return}res,err:=rds.Do("Get","name")//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res:",res)res1,err:=redis.String(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res1:",res1)res2,err:=redis.Int(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res2:",res2)}0
解析:
rds.Do("set", "name1", "abc", "NX", "EX", 5) 执行命令函数,NX命令:在指定的 key 不存在时,为 key 设置指定的值,否则跳过,可以看到过期之后才能继续设置值。
Do(commandNamestring,args...interface{})(replyinterface{},errerror)

6.结构体切片map等等复杂类型缓存处理

funcmain(){RedisInit()deferRedisClose()varerrerror_,err=rds.Do("set","name","abc")//redisset命令iferr!=nil{fmt.Println(err)return}res,err:=rds.Do("Get","name")//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res:",res)res1,err:=redis.String(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res1:",res1)res2,err:=redis.Int(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res2:",res2)}2
得到结果->
funcmain(){RedisInit()deferRedisClose()varerrerror_,err=rds.Do("set","name","abc")//redisset命令iferr!=nil{fmt.Println(err)return}res,err:=rds.Do("Get","name")//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res:",res)res1,err:=redis.String(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res1:",res1)res2,err:=redis.Int(rds.Do("Get","name"))//redisget命令iferr!=nil{fmt.Println(err)return}fmt.Println("res2:",res2)}3
解析:
使用"encoding/json"json.Marshal,把数据转序列化成json数据存入redis,取出key值的时候使用redis.Bytes()解析之后再用json.Unmarshal反序列化得到结果。

三、Redis风险&解决办法

参考文档redis缓存雪崩、穿透、击穿概念及解决办法

1、缓存雪崩

场景

假设缓存上限最多1秒应答100个请求,但是高峰期每秒200个请求,缓存出现意外,导致请求全部作用到数据库,导致数据库挂掉。

解决办法

redis使用高可用的部署,主从、哨兵、集群的模式;

后端限流降级处理;

redis持久化,可快速恢复。

2、缓存穿透

场景

假设缓存上限最多1秒应答100个请求,恶意有人1秒发出100个缓存和数据库都查不到的请求,导致每次都回去查数据库。

解决办法

从数据库中只要没查到,就写一个空值到缓存里。

2、缓存击穿

场景

热点key时效是,大量请求直接作用到数据库。

解决办法

热点数据不过期。

作者:小小小丶叶子著作权归作者所有。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Golang/234.html