为你自己学Redis
  • README
  • 安装
    • macos下安装redis
  • 第1章 构建Redis开发环境
    • 1.1 Redis概述
    • 1.2 了解必要的Docker技能
    • 1.3 安装和配置基于Docker的Redis环境
  • 第2章 实践Redis的基本数据类型
    • 2.1 Redis缓存初体验
    • 2.2 针对字符串的命令
    • 2.3 针对哈希类型变量的命令
    • 2.4 针对列表类型变量的命令
    • 2.5 针对集合的命令
    • 2.6 针对有序集合的命令
  • 第3章 实践Redis的常用命令
    • 3.1 键操作命令
    • 3.2 HyperLogLog相关命令
    • 3.3 lua脚本相关命令
    • 3.4 排序相关命令
  • 第4章 实践Redis服务器和客户端的操作
    • 4.1 Redis服务器管理客户端的命令
    • 4.2 查看Redis服务器的详细信息
    • 4.3 查看并修改服务器的常用配置
    • 4.4 多个客户端连接远端服务器
  • 第5章 Redis数据库操作实战
    • 5.1 切换数据库操作
    • 5.2 Redis事务操作
    • 5.3 地理位置相关操作
    • 5.4 位图数据类型的应用
    • 5.5 慢查询实战分析
  • 第6章 Redis数据持久化操作
    • 6.1 Redis持久化机制概述
    • 6.2 AOF持久化机制实战
    • 6.3 RDB持久化机制实战
    • 6.4 如何选用持久化方式
  • 第7章 搭建Redis集群
    • 7.1 搭建基于主从复制模式的集群
    • 7.2 搭建哨兵模式的集群
    • 7.3 搭建cluster集群
  • 第8章 GO整合MySQL与Redis
    • 8.1 GO通过redigo读写Redis
    • 8.2 Go与各种Redis数据类型
    • 8.3 Redis与MySQL的整合
    • 8.4 Redis缓存实战分析
  • 第9章 Redis应用场景与案例实现
    • 9.1 Redis消息队列实战
    • 9.2 Go实战Redis分布式锁
Powered by GitBook
On this page
  • 8.4.1 缓存不存在的键,以防穿透
  • 8.4.2 合理设置超时时间,以防内存溢出
  • 8.4.3 超时时间外加随机数,以防穿透
  1. 第8章 GO整合MySQL与Redis

8.4 Redis缓存实战分析

Previous8.3 Redis与MySQL的整合Next第9章 Redis应用场景与案例实现

Last updated 1 year ago

8.4.1 缓存不存在的键,以防穿透

代码已经实现.见

8.4.2 合理设置超时时间,以防内存溢出

代码已经实现.见

8.4.3 超时时间外加随机数,以防穿透

我们在之前设置的所有超时时间均为1小时(3600秒).假设我们在某一时刻批量添加了几千个缓存数据,那么按照之前程序设置的超时时间,在1小时之后,这几千个key会同时失效.那么对这批数据的请求会被同时发送到MySQL(当然我们是假定会有对这几千个key的请求),那么MySQL同样有可能崩溃.

解决方法:设置超时时间的数值采用整数 + 随机数的方式

在8.3小节的代码基础上修改:

工程结构如下:

(base) yanglei@yuanhong mysqlAndRedis % tree ./
./
├── biz
│   └── student.go
├── cache
│   ├── conf.go
│   ├── conn.go
│   ├── genExpireSecond.go
│   └── student.go
├── controller
│   └── student.go
├── db
│   ├── conf.go
│   ├── conn.go
│   └── student.go
├── go.mod
├── go.sum
├── lib
│   └── randInt.go
├── main.go
├── request
│   └── student
│       └── getStudentById.go
└── resp
    └── response.go

8 directories, 15 files

lib/randInt.go:

package lib

import (
	"math/rand"
	"time"
)

func GenRandInt(ceiling int) int {
	rand.Seed(time.Now().UnixNano())
	return rand.Intn(ceiling)
}

cache/genExpireSecond.go:

package cache

import "mysqlAndRedis/lib"

const ExpireSecond = 3600

const CeilSecond = 60

func genExpireSecond() int {
	return ExpireSecond + lib.GenRandInt(CeilSecond)
}

cache/student.go:

package cache

import (
	"github.com/gomodule/redigo/redis"
	"strconv"
)

const StudentKeyPrefix = "Stu"

type Student struct {
	Id    int
	Name  string
	Age   int
	Score float64
	Exist bool
}

func (s *Student) FindById(id int) (err error) {
	// 判断键是否存在
	s.Exist, err = s.exists(id)
	if err != nil {
		return err
	}

	// 键不存在则直接返回
	if !s.Exist {
		return nil
	}

	// 确认键存在,则从redis中读取(有可能读到的是一个0值)
	idStr := strconv.Itoa(id)
	key := StudentKeyPrefix + idStr
	reply, err := redis.Values(Conn.Do("LRANGE", key, 0, -1))
	if err != nil {
		return err
	}

	s.Id, err = redis.Int(reply[0], nil)
	if err != nil {
		return err
	}

	s.Name, err = redis.String(reply[1], nil)
	if err != nil {
		return err
	}

	s.Age, err = redis.Int(reply[2], nil)
	if err != nil {
		return err
	}

	s.Score, err = redis.Float64(reply[3], nil)
	if err != nil {
		return err
	}

	// 若读取了该key 则重置过期时间
	// 此处忽略错误
	_ = s.setExpireTime(id)

	return nil
}

func (s *Student) SaveById(id int) error {
	idStr := strconv.Itoa(id)
	key := StudentKeyPrefix + idStr
	_, err := Conn.Do("RPUSH", key, s.Id, s.Name, s.Age, s.Score)
	if err != nil {
		return err
	}

	err = s.setExpireTime(id)
	if err != nil {
		return err
	}

	return nil
}

func (s *Student) exists(id int) (bool, error) {
	idStr := strconv.Itoa(id)
	key := StudentKeyPrefix + idStr
	reply, err := redis.Int(Conn.Do("EXISTS", key))
	if err != nil {
		return false, err
	}

	return reply == 1, nil
}

func (s *Student) setExpireTime(id int) error {
	idStr := strconv.Itoa(id)
	key := StudentKeyPrefix + idStr
	_, err := Conn.Do("EXPIRE", key, genExpireSecond())
	return err
}

运行后查询id = 1和id = 2的数据:

(base) yanglei@yuanhong ~ % redis-cli
127.0.0.1:6379> AUTH redis_user redis_password
OK
127.0.0.1:6379> TTL Stu1
(integer) 3610
127.0.0.1:6379> TTL Stu2
(integer) 3643

可以看到,各个键的生存时间都有所区别了

8.3.4 模拟缓存穿透现象
8.3.5 模拟内存使用不当的场景