1.Redis的基本数据类型
- String
- List
- Set
- Hash
- ZSet
- Bitmap
- Hyperloglog
-
2.Redis底层数据结构
String类型
- 底层数据结构:SDS
- 最大存储大小:512M
- SDS数据结构:len、free、buf[]
- 优点:二进制安全、计算长度快
- Hash类型
- 底层数据结构:Hash表、压缩列表(未来可能被listpack代替)
- Set类型
- 底层数据结构:整数集合、Hash表
- 整数集合数据结构:encoding、contents[]、length
- List类型
- 底层数据结构:压缩列表、双向链表(3.2后使用 quicklist)
- 压缩列表数据结构:每段数据记录前一个记录的大小
- 双向链表数据结构:略
- quicklist数据结构:多个压缩列表的链式化
ZSet类型
String:共享session、分布式锁,计数器、限流
- Hash:缓存用户信息
- List:消息队列,文章列表
- Set:用户标签,生成随机数抽奖、社交需求
- ZSet:排行榜,社交需求(如用户点赞)
- HyperLogLog:基数统计算法统计网站的UV
- Bitmap:用一个比特位来映射某个元素的状态,底层基于字符串实现
-
4.Redis为什么这么快
基于内存存储
高效的底层数据结构
SDS
- 获取字符串长度仅需O(1)时间复杂度
- 二进制安全
- 空间预分配,防止字符串频繁修改导致内存的频繁分配
- 惰性空间释放,不回收多余空间而是使用free记录空闲空间
- 字典
- O(1)的时间复杂度获取key-value
- 跳表
多路I/O复用技术可以让单个线程高效的处理多个连接请求,而Redis使用用epoll作为I/O多路复用技术的实现。并且,Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。
Redis是单线程模型的,而单线程避免了CPU不必要的上下文切换和竞争锁的消耗。但是存在阻塞的风险
Redis6引入多线程提速,但是执行命令仍然是单线程的
虚拟内存机制
虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。通过VM功能可以实现冷热数据分离,使热数据仍在内存中、冷数据保存到磁盘。这样就可以避免因为内存不足而造成访问速度下降的问题。
5.缓存穿透、击穿、雪崩
缓存穿透
- 定义:查询一个数据库中不存在的值
- 产生原因:
- 黑客攻击
- 数据误删
解决方式
定义:某个key在某个时间点过期,但是这一时间点大量的请求过来请求这个key
解决方式:
定义:同一时间大量key过期或者Redis直接崩啦
解决方式:
服务端统计热key
-
解决方式
Redis增加分片副本,均衡读流量
- 热key要均匀分散到不同的Redis服务器中
-
7.Redis的过期策略
过期策略即对达到过期条件key,Redis的处理策略,是立刻删除、还是下次请求时删除?主要有以下几种方式:
定时过期:一旦到达过期时间,立刻CPU执行删除操作(影响性能)
- 惰性过期:当访问key时判断是否过期,过期后删除,极端情况下可能永远删除不掉,导致内存占有
- 定期过期:定时器每隔一段时间扫描expire字典(key->过期时间)中一定量的key进行判断清除
最优解:定期过期+惰性删除,虽然仍然有部分过期key无法删除的风险但是结合内存淘汰策略即可
8.Redis的内存淘汰策略
内存不足时淘汰key的策略:
- allkeys-lru:针对所有key进行最近最久未使用的清理方式
- allkeys-lfu:针对所有key进行最不常用的清理方式
- allkeys-random:所有key随机删除
- volatile-lru:针对有过期时间的key进行最近最久未使用的清理方式
- volatile-lfu:针对有过期时间的key进行最不常用的清理方式
- volatile-random:过期key随机删除
- volatile-ttl:过期key,最先要过期的删除
-
9.Redis常用场景
缓存
- 分布式锁
- 排行榜
-
10.Redis的持久化机制
RDB
流程
- 触发机制
- 开启aof
- 生成文件:*.aof文件
-
11.Redis主从复制原理
slave发送sync(2.8以后psync)请求到master
- master执行bgsave命令,生成rdb文件并发送到slave
- master将RDB生成期间的命令缓存到replication buffer,也发送到slave
- slave载入rdb完成数据同步,对replication buffer中的命令进行执行完成数据的同步
- 同步完成后,master上的新命令会增量复制给从服务器
主从网络断开后,主库会将命令同时写入replication buffer和repl_backlog_buffer。从节点恢复后通过repl_backlog_buffer对数据进行恢复
12.哨兵模式
哨兵的作用
- 监控Redis主从节点的状态
- 主从切换
- 监控自己
- 哨兵工作模式
- sentinel每秒向master和slave发送ping命令
- 若距上次有效回复时间超过阈值down-after-milliseconds,sentinel标记节点为主观下线
- 监控主观下线节点的其他sentinel每秒ping一下该节点
- 足够数量的sentinel认为节点下线时,标记为客观下线
- 正常时,sentinel每10秒向master和slave发送INFO命令
- master客观下线后,每秒向slave发送INFO命令
选主流程
16384个hash槽
- Gossip协议:节点间通信
- 客观下线
- 主节点客观下线:持有槽的主节点投票进行客观下线
- 从节点客观下线:主从节点都可以投票
- 选主:由存活的主节点校验从节点状态、并投票选举
-
14.Redis实现分布式锁的问题分析
setnx+expire
- setnx和expire非原子的,所以可能锁永远解不开->Lua脚本可以解决原子性
- 锁的误解,A加锁超时后B加锁,A执行完解锁。->value值中打标记
- 业务执行超时,锁超时后其他线程进入导致的并发。->守护线程为锁续命
- 不可重入->ThreadLocal记录、Redis Map记录
- 无法等待锁释放->客户端轮询或者发布订阅方式
小结:即使这样,在主从下,主节点加锁,从节点未同步的情况也有问题。红锁的引入解决这个问题
15.Redission锁的实现原理
- 可重入性:加锁通过Hash结构,uuid:次数
看门狗:这里不是观察线程的存活,而是整个服务的存活,只要服务存活,没10s就会为锁续命
16. 红锁
主从下,主节点的锁未同步到从节点,因此会产生问题
红锁实现步骤:获取当前时间(毫秒)
- 顺序向5个master请求加锁,客户端超时时间小于锁超时时间,超时未响应跳过此master
- 客户端轮询完后,计算轮询所费时间,当轮询时长小于锁有效时间,且过半master成功响应则成功上锁
- 成功的话,key的真正过期时间要减去获取锁时间
-
17.MySQL和Redis的数据一致性
缓存延时双删
- 删除缓存重试机制
-
缓存延时双删
先删除老缓存
- 更新数据库
-
删除缓存重试机制
读取biglog异步删除缓存
通过canal,将更新数据发往rocketmq,然后异步的更新缓存
18.Redis的事务
关键命令:MULTI、EXEC、DISCARD 、WATCH、UNWATCH
- EXEC:提交事务
- DISCARD:回滚事务、放弃事务
- MULTI:启动事务
- WATCH:观察某个key,通过结束事务或UNWATCH取消观察
- UNWATCH:取消观察某个key
事务几点:
- 入队过程中有命令错误,整个事务无效
- 入队过程无误,执行中某个命令有错,其他命令执行成功
- 事务结束会将WATCH释放
- 客户端WATCH的key被改掉,事务执行EXEC直接失败
- WATCH在事务开启前进行
参考资料
- https://developer.aliyun.com/article/792320
- https://mp.weixin.qq.com/s/MGcOl1kGuKdA7om0Ahz5IA
- https://mp.weixin.qq.com/s?__biz=Mzg3NzU5NTIwNg==&mid=2247494124&idx=1&sn=c185f7d999d5f006608d05707a8a7eea&chksm=cf2236c5f855bfd329c6e2ee27f23f8131ebcd312960190a10f1a819d67f07a21a08ad17f263&token=162724582&lang=zh_CN&scene=21#wechat_redirect