这是我参与「第五届青训营 」笔记创作活动的第12天。
Redis
什么是Redis
为什么需要Redis
- 数据库从单表到分表分表
- Mysql从单机演变到集群:数据量增长、读写压力大
- 数据分冷热:将热数据储存在内存中
Redis原理
- 从内存中读写数据
- 数据保存到硬盘防丢失
- 单线程处理命令
应用案例
连续签到
用户每日有一次签到机会,若签到成功,则累积签到加一,若断签,则清零。
使用redis过期
func addContinuesDays(ctx context.Context, userID int64) {
key := fmt.Sprintf(continuesCheckKey, userID)
// 1. 连续签到数+1
err := RedisClient.Incr(ctx, key).Err()
if err != nil {
fmt.Errorf("用户[%d]连续签到失败", userID)
} else {
expAt := beginningOfDay().Add(48 * time.Hour)
// 2. 设置签到记录在后天的0点到期
if err := RedisClient.ExpireAt(ctx, key, expAt).Err(); err != nil {
panic(err)
} else {
// 3. 打印用户续签后的连续签到天数
day, err := getUserCheckInDays(ctx, userID)
if err != nil {
panic(err)
}
fmt.Printf("用户[%d]连续签到:%d(天), 过期时间:%s", userID, day, expAt.Format("2006-01-02 15:04:05"))
}
}
}
消息通知
List数据结构:QUICKLIST
双向链表+ListPack实现:
listpack可以包含多个元素,达到节省内存的作用。
计数
一个用户有多个数据需要计数,可以使用hash结构存储
若使用数据库表记录,性能过差,查询效率低
hash结构
排行
zset结构
跳跃表:
增强链表的访问速度,通过跳跃表实现。将长链分成多条子链。
而zset是跳跃表加hash的实现方式
分布式锁
要求并发场景下,多个协程只能有一个协程执行,执行完成后,其他等待中的协程才能执行。
利用了redis中的两个特性:
- Redis单线程执行命令
- setnx只有未设置过才能执行成功
注意事项
大key和热key
string:认为value大于10KB的叫大key
Hash、set、list等复杂数据结构,元素大于5000或者总value大于10MB则认为是大key
大key的危害:
- 读取慢
- 容易导致慢查询(过期)
- 主从复制异常服务阻塞
用户访问某数据频率很高,可以认为是热key
处理方法:localCache
慢查询
- 批量传入过多查询
- 大key
- zset过大导致性能下降
缓存穿透/缓存雪崩
缓存穿透:热点数据查询绕过缓存直接查询数据库
缓存雪崩:大量缓存同时过期
解决方法:返回控制、布隆过滤器
小结
本节课程主要讲述了三个方面问题:为什么需要Redis,Redis的基本工作原理、Redis应用案例以及在字节跳动,使用Redis有哪些注意事项。