五大基础数据结构:
- List;Set;ZSet;Hash;String
- 后续添加了如BitMap;Stream等数据结构
跳表:
ZSet的底层数据结构就是跳表,简单来说就是:
在一个基础链表之上新增几条链表,将基础链表的数据以抛硬币的方式决定这个数据在第几层。
然后链表的遍历从最上面的链表开始,有点类似二分查找。
ZSet为何选择跳表而非B+树:
因为B+树的最大优点就是减少磁盘IO,而Redis是纯内存操作。
比任何树的代码都更加简单
更新操作时只需要修改指针,而不用树的自旋平衡。
Redis中的Hash表是怎样扩容的:
创建一个新的哈希表,因为Redis是单线程的,所以数据量很大的时候可能会引发阻塞。所以只有在新的更新操作执行前,Redis会偷摸的进行一次顺序的数据搬迁,直至完成数据搬迁。扩容过程中的所有操作都会在两张表上执行,比如查询操作:查询旧表,若无结果则去查询新表,而新增操作只在新表。
String的数据结构:
在Redis中String用SDS结构存储,而非普通的字符串。
SDS内部维护了:len(字符串长度)+alloc(空间长度)+flags(SDS类型)+buf(内容)
当需要对字符串读取、操作时可以单独操作某个结构,速度快。
Redis为什么快:
- Redis是基于内存操作的,并且数据结构高效。它的性能瓶颈是内存和带宽,而非CPU,自然选择单线程就够。
- Redis单线程模型省去了多线程的线程切换开销和竞争
- 采用IO多路复用技术:(多路:多个客户端;复用:复用同一个线程)
- 维护一个主事件监控器,当某个服务器请求IO到达的时候,内核会将它放进就绪队列,并通知事件处理进行处理,从而避免了无效阻塞
Redis的原子性:
对于Redis自身执行命令来说,因为是单线程所以是没有并发问题的
但是在程序调用Redis的过程中是有并发问题的,而原子性可以通过Lua脚本来解决
虽然Redis自身有提供事务功能,但是其不具备回滚的能力,大部分场景都不适用。
日志:
Redis是基于内存进行操作的,所以当掉电或重启时数据会丢失。
所以可以通过三种方式进行刷盘(持久化到硬盘):
- AOF日志:当Redis执行一条操作命令时,就以追加的方式写入到日志文件中
- RDB快照:将某时刻的内存数据统一以二进制的方式写入到硬盘之中
- 混合持久化:在AOF日志重写前(AOF日志体积过大),先以RDB的方式将内存数据写进日志文件,再执行AOF的追加写操作。重启时先重放RDB部分,再回放AOF的操作命令。
AOF日志:
有三种执行策略:
- Always:一旦操作命令完成时,立即写入磁盘
- EverySecond:每隔一秒写入一次
- No:由操作系统自行决定,一般是cache写满时
优点:数据安全性强,不易丢失。追加写方式,性能好。有重写机制,体积适中
缺点:因为记录的都是操作,回放的时候需要重新写一遍,性能开销巨大
RDB快照:
通过快照的方式记录某一时刻的内存数据,备份恢复的速度很快。
快照操作由子进程进行快照记录,不阻塞主线程。因为是定时任务所以体积比AOF小。
缺点是两个快照之间的数据会丢失。
Redis的缓存淘汰和过期删除:、
缓存淘汰:
- 对有TTL的数据进行淘汰:
- 随机淘汰;最久未使用;最不常用;最早过期;
- 对所有数据范围进行淘汰:
- 随机淘汰;最久未使用;最不常用
过期删除:
- 惰性删除:
- 当对这个key访问或修改时,先查询TTL是否过期,是则删除,返回null
- 定期删除:
- 定期从数据库随机抽查一部分key,并删除TTL过期的Key,如果删除的Key大于1/4,则继续抽查
而Redis默认是采用两者都用的方式。
Redis集群:
主从复制过程:
- 全量同步:
- 1.从库向主库发起请求
- 2.主库接收后进行RDB快照,并将快照传输给从库
- 3.同时在传输过程中会记录AOF。后将AOF传给从库
- 4.从库接收后清空当前数据后载入快照数据,并根据AOF回放数据,以保证主从的数据一致性
- 增量同步:允许断点续传
- 主库的写操作命令传给从库时,不仅会将写命令传输给从库,还会同步传输给缓冲池
- 当从库断网重连后,会从缓冲池中读取命令数据。
- 主库从库通过缓存池中各自的偏移量记录传输进程,以实现断点续传。
全量同步的开销比较大,所以推荐扩大缓冲池内存以采用增量同步的方式
tips:主从库一般为读写分离,主库负责写,从库负责读。因为集群有不可避免的数据一致性问题,所以写操作必须由主库负责。
哨兵机制:
设立专门的哨兵节点,他会监控主节点是否故障,若故障则立即选择从节点选举为主节点,并且通知其他所有节点。
哨兵机制的主节点选举算法:
当一个哨兵发现主节点挂了(主观下线),会询问其他哨兵,如果超过一定数量的哨兵都认为主节点挂了,此时判定为客观下线。
之后通过投票来选举领头哨兵(Leader)【总票数要一半以上,谁先发请求就投给谁】。领头哨兵会通过一些条件从从节点中筛选出新的主节点。
tips:哨兵集群至少需要3个,因为成为Leader至少需要一半以上(3/2=1.5→2票),这样如果一个哨兵挂了至少也能通过投票
大Key问题及其解决办法:
- 问题:
- 占内存大,可能引发Redis使用缓存淘汰策略而导致有用的key被淘汰,甚至内存爆满
- 内存碎片大,拖慢Redis性能
- 对大Key的读写费劲,阻塞线程
- 主从复制的开销变大
- 解决办法:
- 对大Key进行取模等操作,将一个大Key分散到多个key中
- 定时清理维护Key
热Key问题及其解决办法:导致单个Redis节点过热甚至导致崩溃
- 解决办法:
- 将热Key通过主从复制的方式拷贝到从节点中,从而分散热度
- 采用读写分离架构,主节点只做写操作,从节点承担读操作
Redis和MySQL数据一致性问题:
缓存是通过牺牲强一致性来提高性能,不适用于强一致性的场景
- 读操作:如果未命中缓存,则先读取数据库,再写入缓存
- 写操作:先更新数据库,再删除缓存



