我们在之前设置的所有超时时间均为1小时(3600秒).假设我们在某一时刻批量添加了几千个缓存数据,那么按照之前程序设置的超时时间,在1小时之后,这几千个key会同时失效.那么对这批数据的请求会被同时发送到MySQL(当然我们是假定会有对这几千个key的请求),那么MySQL同样有可能崩溃.
(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
package lib
import (
"math/rand"
"time"
)
func GenRandInt(ceiling int) int {
rand.Seed(time.Now().UnixNano())
return rand.Intn(ceiling)
}
package cache
import "mysqlAndRedis/lib"
const ExpireSecond = 3600
const CeilSecond = 60
func genExpireSecond() int {
return ExpireSecond + lib.GenRandInt(CeilSecond)
}
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
}
(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