这是我参与「第五届青训营」笔记创作活动的第14天
1. 本堂课重点内容
- 为什么需要redis,redis的基本工作原理
- Redis应用案例
- 在字节跳动,使用redis有那些主要事项
2. 为什么需要redis?
数据量小的时候我们可能使用单个数据库表就可以了,数据量增长,我们可以使用分库,分表,数据库集群,但是当数据量特别大的时候,如秒杀,抢票...,数据库的读写压力不断增大。
- 数据从单表演进出了分库分表
- Mysql从单机演进出了集群
- 数据量增长
- 读写数据压力不断增加
- 数据分冷热
- 热数据:经常被访问的数据
- 将热数据存储到内存中
3. redis的基本工作原理
- 数据从内存中读写
- 数据保存到硬盘上防止重启数据丢失
- 增量数据保存到AOF文件
- 全量数据RDB文件
- 单线程处理所有操作命令
4. redis应用案例
4.1 连续签到(String数据类型)
- key
- value
- expireAt
String数据结构
- 可以存储字符串、数字、二进制数据
- 通常和expire配合使用
- 场景:存储计数、Session
4.2 消息通知 (List数据结构)
- 使用场景:例如文章更新时,将更新后的文章推送到ES,用户就能搜索到最新的文章数据
List数据结构Quicklist
- Quicklist由一个双向链表和listpack实现
- Listpack数据结构
4.3 计数 (哈希数据结构)
- 一个用户有多项计数需求,可通过hash结构存储 Hash数据结构dict
- rehash
- 渐进式rehash
4.4 排行榜(Zset数据结构)
- 积分变化时,排名要实时变更
- 结合dict后,可实现通过key操作跳表的功能
- ZINCRBY myzset 2 "Alex"
- ZSCORE myzset "Alex"
Zset数据结构
4.5 限流
- 要求一秒放行的请求为N,超过N则禁止访问
4.6 分布式锁
并发场景,要求一次只能由一个协程执行,执行完成后,其他等待中的协程才能执行。
可以使用redis的setnx实现,利用了两个特性
- redis是单线程执行命令
- setnx只有未设置过才能执行成功
5. redis注意事项
5.1 大key、热key
5.1.1 大key的定义
5.1.2 大key的危害
- 读取成本高
- 容易导致慢查询(过期、删除)
- 主从复制异常,服务阻塞,无法正常响应请求
- 业务侧使用大key的表现
- 请求redis超时报错
5.1.3 消除大key的方法
1.拆分
- 将大key拆分为小key,例如一个String拆分成多个String
- 压缩
- 将value压缩读取解压后再使用
5.2 热key
5.2.1 热key的定义
用户访问一个key的QPS特别高,导致server实例出现cpu负载突增或者不均的情况,热key没有明确的标准,QPS超过500就有可能被识别为热key
5.2.2 解决热key的方法
- 设置Localcache 在访问redis前,在业务服务侧设置Localcache, 降低访问redis的QPS.LocalCache中缓存过期或未命中,则从redis中将数据更新到LocalCache.
2. 拆分
将key:value这一个热key复制多份使用
- 字节跳动如何解决
5.3 慢查询场景
- 批量操作一次性传入过多key/value, 如mset/hmset/sadd/zadd等O(n)操作,建议单批次不要超过100,超过100之后性能下降明显
- zset大部分命令都是O(log(n)), 当大小超过5k以上时,简单的zadd/zrem也可能导致慢查询
- 操作的单个value过大,超过10kb
- 对大key的delete/expire操作也可能导致慢查询
5.4 缓存穿透、缓存雪崩
- 缓存穿透:热点数据查询绕过缓存, 直接查询数据库
- 缓存雪崩:大量缓存同时过期
5.4.1 缓存穿透的危害
- 查询一个一定不存在的数据 通常不会缓存不存在的数据,这类查询请求会直接打到db,如果有系统bug或人为攻击,那么容易导致db响应慢甚至宕机
- 缓存过期时 在高并发场景下,一个热key如果过期,会有大量请求同时击穿至db,容易影响性能和稳定。同一时间大量key集中过期,也会导致大量请求落到db上。
5.4.2 减少/避免缓存穿透
- 缓存空值 如果不存在的userId,这个id在缓存和数据库中都不存在,则可以缓存一个空值,下次查直接返回
- 布隆过滤器
5.4.3 如何避免缓存雪崩
- 缓存空值 将缓存失效时间分散开,比如在原有的失效时间上面增加一个随机值。
- 使用缓存集群,避免宕机造成的缓存雪崩