Redis是什么
Remote Dictionary Server,远程字典服务。
- 可基于内存可持久化的日志型、Key-Value数据库。
- 数据都是缓存在内存中
- Redis会周期性把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并在此基础上实现了主从同步(RDB/AOF?)。
五大基本数据类型
String
- 最大存储为512MB
- 字符串可以是简单字符串、复杂xml/json字符串、二进制图像音频字符串、也可以是数字字符串
应用场景
缓存
元数据配置预载
状态标志
计数器
List
存储多个有序的字符串,列表中每个字符看做一个元素
支持存储2^32次方-1个元素
- 可以从列表的两端进行插入和弹出元素
- 可以读取指定范围的元素集
- 可以读取指定下标的元素
- 可以充当队列(左进右出)和栈(后进先出)的角色
应用场景
消息队列
可以使用左进右出的命令来完成队列的设计
文章列表或者数据分页展示的应用
列表有序且可以支持范围获取,可以完美解决分页查询的功能。
Set
- list和set的区别就在set不能存储重复元素,没有元素下标。
- 支持多个集合的交集、并集、差集操作
应用场景
标签
博客网站使用到的标签功能,可以把关注类似内容的用户利用一个标签进行归并
共同好友
利用交集的功能
统计网站的独立IP
利用set的不可重复特性,可以统计此类数据
Sorted set(有序集合)
- 保留了集合中不能重复的特性
- 为每个集合设置了一个分数,可以利用该分数作为排序的依据
应用场景
- 排行榜
按照要进行排序的条件,将每个元素符合条件的数目做为分数保存。
hash
- redis本身是个Key-Value型数据库,hash相当于在value中又嵌套了一层Key-Value型数据
应用场景
- 存储关系型数据库的表记录
底层数据存储结构
Redis整体的存储结构
- 整体是一个大型的hashmap
- 内部时数组实现的hash,key冲突通过挂链表实现。
- 链表中每个元素为一个key/value对象,value中存储RedisObject
- RedisObject中有三项,一是ptr指针指向具体数据结构的地址,二是type为对象类型,三是对象底层编码

String的数据结构
类型编码的转换顺序
- 保存的值为整数且在long(2^32)以内的范围,都是用整数存储
- 当字符串长度不超过44字节,使用EMBSTR编码
- 当字符串长度大于时,使用RAW编码
SDS(后续补充)
List存储结构
版本区别
- 3.2版本之前是zipList和linkedList
- 3.2版本之后是quickList
压缩列表(zipList)
概述
- 一种压缩链表
- 存储内容都是在连续的内存区域,更能节省空间
- 列表元素不大,每个元素也不大时,适合采用zipList存储
条件限制:
- list中每个元素的长度小于64
- 列表中数据量少于512个
提问:为什么大数据量不适合用zipList?
- 因为zipList是一段连续的内存,每当插入新元素都需要做内存扩展
- 如果超出zipList的内存大小,还会重新分配内存空间,并将内容复制到新的地址
- 如果数据量比较大,重新分配内存和拷贝内存耗时过久
数据结构
redisObject对象结构中ptr指向的就是一个zipList,整个redis在内存中是一块连续的区域,只需要malloc一次即可。

linkedList
快速列表(quickList)
概述
- 基于zipList和linkedList结合
- 将linkedList按断切分,每段都是一个zipList
- 多个zipList之间使用双向指针链接(双向链表)
数据结构

扩展
- zipList的长度
- 压缩深度
Hash类型
- 当Hash中数据项比较少的情况下,底层使用压缩列表
- 随着数据的增多,底层会转为dict
具体数据项多少会发生转变
hash-max-ziplist-entries(数据项数量):512
hash-max-ziplist-value(单个数据长度):64
dict
数据结构

- 一共两个节点,每个节点下是一个HashTable
- 通常情况下只有一个节点是有值的
- 在扩容时,后面节点生成新的hashTable,并逐步将老节点的数据重新放到新节点中,完成后删除老节点
reHash步骤

Set数据结构
概述
- 相当于Java中的hashSet
- 内部键值对无序且唯一
- 所有的value均为null
- 底层编码有两种,一种为hashTable,一种为inset
inset
- 存储的数据都是整数
- 存储的元素个数小于512个
Zset
- zset给每个元素设置了一个分数,用来作为排序的依据
- 集合内不能有重复成员
- 底层有两种,一是zipList,二是skipList
zipList
- 每个元素使用两个紧挨在一起的压缩列表节点来保存
- 第一个节点保存元素,第二个节点保存分值

如图:按照学生成绩排序,每个学生占用两个节点,一位学生标志,二为学生成绩
(跳表)skipList
跳表的产生
有序链表:

在这种结构下,想要找到一个元素,必须要进行遍历。
假如我们在相隔的节点上再增加一个指针,隔空链接。
两层有序跳表

此时,想要找到一个元素就不必遍历所有节点。先跳跃查询,找到适当的节点后再回查即可。类似二分查找。
三层有序跳表
如果按照此思路,还可以增加三层跳表

问题:
如果链表需要插入新的元素,那么原有跳表的结构就被打乱,如果保持不变,那么在更新过程中,跳表就会被破坏,失去效率优势。
skipList的解决方案
在有序跳表的基础上,做了改进,不严格限制跳表的结构。当插入新的节点时,都会随机生成一个层数,更新节点时不需要对其他节点进行处理

Redis持久化
避免服务器进程退出,数据丢失,redis提供了两种持久化方案。
- 快照(RDB)
- 日志(AOF)
对比

RDB持久化
- 在特定间隔保存对应时间节点的数据快照
- 生成的文件为压缩二进制文件,可以通过该文件还原数据库状态
工作原理
- 当需要进行RDB时,主进程不会做IO操作
- redis会挂起一个子进程将数据集写入临时RDB文件中
- 当新RDB文件写入完成后,就会替换旧的RDB文件,并删除旧文件
- 如果有新数据写入,redis会开辟新的空间并将元数据复制出来写入到复制的那份数据中(COW)
COW(copy-on-write)

触发机制
主动触发
- save:同步命令,会阻塞进程
- bgsave:异步命令,可继续响应请求

自动触发
时间配置,多少秒内修改了几次key,就触发。
优缺点
优点
- 二进制文件,内存小,适合备份
- 更快传输至服务器进行数据恢复
- 更快重启
缺点
- 定点备份,可能会丢失数据
- 如果数据量非常大时,每次RDB写入磁盘时,会占用CPU,造成客户端服务卡顿
AOF
- 以日志形式记录写操作,记录所有指令
- 只能追加,不可修改
- 重启时按顺序进行逐个命令恢复
AOF配置
重写机制
如果每条记录都保存,那么日志中会有很多无意义的记录。如将一个数据累计增加1000次,那么只需要记录最后的值即可,没必要记录中间过程的每一个值。
- 重写指令会开辟一个新的子进程对内存进行遍历
- 转换为新的操作指令
- 转换完成后序列化到一个新的日志文件中
- 替换原有的AOF文件
触发方式
手动触发
手动命令触发
命令
- bgrewriteaof
自动触发
根据配置规则进行触发
配置
- no-appendfsync-on-rewrite 开启重写
- auto-aof-rewrite-percentage 100 文件增量为一倍后触发重写
- auto-aof-rewrite-min-size 64mb 触发重写的最小文件大小
优缺点
优点
- 数据安全,每个操作都可以记录
- 通过append模式追加数据,服务器宕机也可以解决数据一致性问题
- 重写机制
缺点
- 文件要更大,恢复速度慢
- 数据集过大时,启动效率低
事务?
发布订阅?
主从复制
- 将一台服务器的数据复制到其他服务器
- 单向复制
- 只能由主节点到从节点
- 默认情况下,一个主节点可以有多个从节点,一个从节点只能有一个主节点
主从复制用来做什么?
- 数据冗余
- 故障恢复:当主节点出现问题时,由从节点提供临时服务
- 负载均衡:配合读写分离,可以分担服务器负载
- 高可用:哨兵和集群部署的基础
分类
- 全量复制:第一次复制
- 增量复制:把主从网络断连期间主库收到的命令,同步给从库
全量复制
缓存穿透、击穿、雪崩
缓存穿透
概念
穿透是指客户端请求的key在缓存中没有,直接请求到数据库,然后数据库中也没有。此时用户还在不断发生请求,由于数据库中也没有该数据,导致用户请求一直在访问数据库,流量过大时,数据库就可能宕机。
解决方案
- 接口层直接校验,拦截不合法数据;
- 缓存中获取不到的值,在数据库中也获取不到,就把对应的key的值置为null并写入缓存中,有效期设置30秒左右
- 布隆过滤器:类似一个hashset,可以快速判断某个元素是否存在在集合中,不存在直接返回
缓存击穿
概念
击穿是指缓存中没找到但是数据库中有数据的情况,一般是因为缓存的key超时,此时如果并发量大时就会造成数据库压力。
解决方案
- 设置热点数据永不过期
- 接口限流、熔断、降级:重要接口要做好限流,并要有降级准备,当某些服务不可用时,及时熔断,快速返回;
- 加互斥锁,避免并发
缓存雪崩
概念
与击穿类似,不同在于击穿是并发获取同一数据,而雪崩是指缓存中大批量的数据同时过期,造成数据库压力激增甚至宕机。
解决方案
- 缓存数据的过期时间随机,避免在同一时间出现大量数据过期
- 热点数据永不过期
参考
https://blog.csdn.net/Rong_Toa/article/details/116849170
阿里开发者手册-Redis专题
https://blog.csdn.net/Ye_GuoLin/article/details/115951006