ACP
Redis是 AP ,Redis的一致性模型是最终一致性
数据类型
- 字符串(String)
- 哈希(Hash)
- 列表(List)
- 集合(Set)
- 有序集合(Sorted Set)
高级数据类型:Streams、Bitmap、Geospatial以及HyperLogLog
String
Redis 自己定义了 SDS(Simple Dynamic Strings,简单动态字符串), 为了存储任意字符串(无需'\0'结尾),增加 alloc 表示分配的总长度**,len** 表示当前字符串长度**,提高各个操作的效率。**
Sorted Set
- 早期采用 ziplist(压缩列表) 和 skiplist(跳跃表) 两种结构,Redis 5.0 引入 listpack(紧凑列表)取代 ziplist
- 元素少时使用 ziplist 或listpack节约内存。默认 128 个元素,每个元素小于 64 字节
- skiplist其实就是多个索引层级的有序链表
ziplist
级联更新问题
ziplist 中的 Entry(实际存储数据项) 由三部分组成 prevlen (记录前一个entry的长度)、 encoding(内容编码)、content(实际数据)
prevlen:前一个entry长度 <254时占用1字节, ≥254:占用5字节(首字节固定为0xFE)
假设当前 ZipList 中包含3个 Entry,每个 Entry 的总长度均为253字节。此时在 Entry1 后插入一个长度为300字节的新 Entry,将触发连锁反应后面所有的 prevlen 都需要更新,导致显著的性能损耗。
listpack
如何解决级联更新问题
废弃原有 prevlen 字段,引入 backle n字段记录整个 Entry 的字节数
集群模式
- 主从模式
- 哨兵模式
- Cluster模式(集群模式)
Cluster
数据分片
在Redis的Cluster 集群模式中,使用哈希槽 (hash slot)的方式来进行数据分片,将整个数据集划分为16384 个槽,每个节点负责多个槽,通过 CRC16 算法得出数据在哪个槽
为什么是16384
2的幂次方,可以使用位运算计算。官方在实践中得出比较合适的值
持久化机制
- RDB 是定时备份,体积小,恢复速度快。
- AOF 是实时备份,可靠性高。
- 通常使用 RDB-AOF 混合持久化。先将数据以 RDB 格式写入 AOF 文件开头
写回策略
- RDB分自动触发(可配置)和手动触发
- AOF 分 Always(同步写回)、 Everysec(每秒写回) 、No(操作系统控制), 一般用Everysec
过期策略
定期删除 和惰性删除相结合的方式
内存淘汰策略
- 用于内存满了之后,决定哪些key淘汰
- 默认是 noeviction不淘汰,直接报错
- 腾讯建议做为缓存使用时,用 allkeys-lru (从所有 key 中选择最近最少使用的那个 key 并删除),做为半持久化半缓存时,使用 volatile-lru (从设置了过期时间的 key 中选择最近最少使用的那个 key 并删除)
- 阿里云默认 volatile-lru , 腾讯云默认 noeviction
缓存失效算法
- FIFO先进先出
- LRU最近最少使用
- LFU最少频次使用,记录每个key的使用次数,很占内存
- W-TinyLFU Caffeine中的算法,结合 LRU 和 LFU ,由窗口缓存 、过滤器 和主缓存组成
- 窗口缓存采用 LRU 为新元素提供积累访问频率的机会
- 在过滤其中与主缓存淘汰出来的 Key 进行比较
快的原因
- 基于内存
- 单线程模型
- 高效的数据结构
- IO多路复用技术
- 6.0引入多线程处理网络请求
引入多线程的原因
单线程支持QPS 8万-10万,瓶颈在网络IO的处理上
Lua 脚本
原子性
Redis 会将 Lua 脚本封装成一个单独的事务在Redis客户端运行,如果在这个进程中有其他客户端请求的时候,Redis 将会把它暂存起来,等到 Lua 脚本处理完毕后,才会再把被暂存的请求恢复。
事务机制
Redis的事务是不支持回滚的
和 Lua 比较
- 都保证原子性、都不能回滚
- 事务发生错误不影响后续的命令执行,Lua影响
- 事务多次网络交互,Lua 只有一次
- 事务不能获取其他命令的中间结果,Lua 可以
通信协议
RESP 协议( 自己设计的,基于 TCP 协议**)**
Pipeline
一种优化网络延迟的技术,客户端可以将多个命令一次性发送至 Redis 服务器,无需等待每个命令的响应
与 Memcached 区别
- Memcached 只支持简单的键值对存储,不支持持久化,只能手动分片
- Memcached 使用多线程处理数据,只支持基本的 GET、SET 操作
- Memcached 使用文本协议,只支持一个默认数据库
Redis 适用于数据结构复杂、需要高级功能和数据持久化的场景;而 Memcached 则适用于简单的键值存储场景。
常见问题
热key
同一时间点同一个key被大量访问,就会导致流量过于集中,使得很多物理资源无法支撑。
如何解决
热key拆分、多级缓存、限流
热key标准
QPS集中、带宽集中、CPU集中
结合业务而定,京东有hotkey框架可以根据业务随时调整热key的访问频率
识别热key
经验预判、实时监测(埋点、代理层统一收集、redis自带命令)
大Key
这个Key对应的value占用空间很大或者元素很多
危害
影响性能、占用内存、集群中内存分布不均、备份迁移恢复困难等
如何解决
大 Key 拆分、删除大 Key、设置合理的过期时间防止膨胀
识别大key
自带命令 redis-cli --bigkeys
缓存击穿
某一key的缓存过期时大并发量的请求同时访问此key,瞬间击穿缓存服务器直接访问数据库,让数据库处于负载的情况。
如何解决
异步定时更新、互斥锁(如果缓存没有先上锁,从数据库获取数据上缓存后再释放)
缓存穿透
缓存服务器中没有缓存数据,数据库中也没有符合条件的数据,导致业务系统每次都绕过缓存服务器查询下游的数据库,缓存服务器完全失去了其应有的作用。
如何解决
缓存空值、布隆过滤器
缓存雪崩
大量缓存同时过期或缓存服务宕机,所有请求的都直接访问数据库,造成数据库高负载,影响性能,甚至数据库宕机。
如何解决
设置不同的过期时间(加随机数)、集群部署
集群模式中使用事务和 Lua 的限制
执行 Lua 脚本时,脚本中访问的所有键也必须位于同一节点
如何解决
默认情况下,Redis 使用键的哈希值 来决定将数据存储在哪个节点。我们可以通过 HashTag来干预哈希值的生成