Redis|青训营笔记
这是我参与【第五届青训营】伴学笔记创作活动的第15天。
一、重点知识
- redis的简介,内存/硬盘
- 应用案例:典型数据结构
- redis可能发生的问题,解决方法
二、详细知识点
1. Redis简介
1.1 为何需要Redis
- 数据从单表演进出分库分表
- MySQL从单机演进出集群
- 数据量增长:数据更多
- 读写数据压力不断增加
- MySQL无法应对压力
- 数据分冷热:热数据经常被访问
- 因此将热数据放在内存中会减轻磁盘读取压力
- 先检查内存中是否存在
- 若miss,去数据库读取
- 读取的数据写入redis中
1.2 工作原理
- 数据从内存中读写
- 数据保存到硬盘上防止重启数据丢失
- 增量数据保存到AOF文件
- 全量数据保存到RDB文件
- 单线程处理所有操作命令
2. Redis应用案例
2.1 连续签到
- key:uid; value:252; expireAt:23:59:59
- 设置过期
- String数据结构
- sds
- 可以存储字符串、数字、二进制数据
- 需要节省空间
- 需要很快的写入和读取
- 通常和expire配合使用
- 场景:存储计数、session
2.2 消息通知
- 用list作为消息队列
- 使用场景::消息通知,文章更新后将更新的文章推送到ES
- 可以用消息队列或pub和sub
- list数据结构
- 监听链表头部pop
- 用户推送push进入list
- 实现一个队列
- quicklist
- 双向链表
- listpack:一个节点上有大量的数据(LinkedArrayList)
- 通过读total byte获得长度
- 通过读num-elements获得数量
- 之后为值
- 定长+变长
2.3 计数
- 多项计数需求,可以通过hash结构存储
- 每个数据使用一个key来读取,即hash
- hash数据结构
- 使用的时hash表+链表结构
- 扩容时需要rehash
- rehash:将ht[0]中的数据全部迁移到ht[1]中;数据量小时就很快,但数据量大时会明显阻塞用户请求
- 渐进式rehash:为避免出现这种情况,使用了rehash方案,基本原理就是每次用户访问时都会迁移少量数据,整个迁移的过程平摊到所有访问请求过程。
2.4 排行榜
- 积分变化时排名实时变更
- 结合dict后可以实现通过key操作跳表的功能
- zset数据结构 zskiplist
- 查找数字路径 head 3,3,7
- 将数据分成多条子链
- redis时跳跃表+hash
2.5 限流
- 一秒内放行请求为N,超过则禁止访问
- 对key调用incr,超过N则禁止访问
- 构建key+当前时间戳,当后续时间戳大于前置时间戳时停止访问
2.6 分布式锁
- 并发场景要求一次只有一个协程执行
- 执行完成后其他等待的才能开始执行
- redis的setnx实现
- redis是单线程执行命令
- setnx只有未设置过才能成功
3.Redis使用注意事项
3.1 大Key
- 大Key标准
- String:value字节大于10kb
- hash/set/Zset/list等:元素个数大于5000,或总字节大于10MB
- 大Key危害
- 读取成本高
- 容易导致慢查询(过期、删除)
- 主从复制异常,服务阻塞无法正常响应请求
- 业务侧使用大key表现
- 请求redis超时报错
- 消除大key的方法
- 拆分:将大key拆分为小key,例如一个String拆成多个String
- prefix通知当前拆分量
- 压缩
- 将value压缩后写入redis,读取是解压后再使用
- 压缩算法:gzip、snappy、lz4
- 一个压缩算法压缩率高,解压耗时长
- 需要对实际数据测试后选择合适算法
- JSON:MessagePack序列化
- 集合类结构
- 拆分:hash取余,位掩码方式决定放在哪个key中
- 区分冷热:榜单列表场景使用zset只缓存前10页数据,后续数据走db
- 拆分:将大key拆分为小key,例如一个String拆成多个String
3.2 热key
- 定义
- 用户访问一个key的qps特别高
- server出现CPU负载突增或不均情况
- 没有明确标准,qps超过500则为热key
- 解决方法
- 设置localcache:在业务服务侧设置localcache,降低访问redis的qps,localcache缓存过期或未命中则从redis中将数据更新到localcache
- java:Guava
- 拆分:将KV这个热key复制写入多份,K1V, K2V,访问时候访问多个key以此将qps分散到不同实例上降低负载;但是更新时需要更新多个key,存在数据不一致风险
- 使用Redis的代理热key承载能力:使用redis代理,具备热key承载能力,结合了热key发现,localcache两个功能;proxy统计一个key在一定时间内读取的次数,如果发现热key则存到proxy的localcache;
- 设置localcache:在业务服务侧设置localcache,降低访问redis的qps,localcache缓存过期或未命中则从redis中将数据更新到localcache
3.3 慢查询
- 容易导致的操作
- 批量操作:一次传入过多的KV,如批量加入,不要单批超过100
- zset大小不要超过5k
- 单个value过大 大key
- 对大key的删除和过期会阻塞redis
3.4 缓存穿透
- 缓存穿透:热点数据查询绕过缓存直接查询数据库
- 缓存穿透危害
- 查询一个一定不存在的数据:通常不会缓存不存在的数据,请求直接打到db,容易导致db响应慢、宕机;
- 缓存过期时:高并发场景下一个热key如果过期会大量请求击穿至db,影响db
- 同一时间大量key过期时也会导致大量请求落到db上
- 减少缓存穿透
- 缓存空值:如一个不存在的值,缓存一个空值下次再查询直接返回空
- bloomfilter:存储合法key
3.5 缓存雪崩
- 缓存雪崩:大量缓存同时过期
- 避免方法
- 缓存空值:将缓存失效时间分散开,在原有时间基础上加随机值;热门时间过期时间长一点,冷漠短一点
- 使用缓存集群。
三、个人总结
今天主要学习了redis的实际应用,以及各种数据结构的底层逻辑;最后介绍了redis的问题以及解决方法。课程是从比较抽象的角度进行讲解,需要结合代码进一步理解。