Redis
数据类型、底层结构以及使用场景
Redis 提供了丰富的数据类型,常见的有五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)
随着 Redis 版本的更新,后面又支持了四种数据类型: BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。
应用场景:
- String 类型的应用场景:缓存对象、常规计数、分布式锁、共享 session 信息等。
- List 类型的应用场景:消息队列(但是有两个问题:1. 生产者需要自行实现全局唯一 ID;2. 不能以消费组形式消费数据)等。
- Hash 类型:缓存对象、购物车等。
- Set 类型:聚合计算(并集、交集、差集)场景,比如点赞、共同关注、抽奖活动等。
Zset 类型:排序场景,比如排行榜、电话和姓名排序等。
BitMap(2.2 版新增):二值状态统计的场景,比如签到、判断用户登陆状态、连续签到用户总数等;
- HyperLogLog(2.8 版新增):海量数据基数统计的场景,比如百万级网页 UV 计数等;
- GEO(3.2 版新增):存储地理位置信息的场景,比如滴滴叫车;
- Stream(5.0 版新增):消息队列,相比于基于 List 类型实现的消息队列,有这两个特有的特性:自动生成全局唯一消息ID,支持以消费组形式消费数据。
字符串是最基本的数据类型,可以存储文本、数字或者二进制数据,最大容量是 512 MB。适合缓存单个对象,比如验证码、token、计数器等。
列表是一个有序的元素集合,支持从头部或尾部插入/删除元素,常用于消息队列或任务列表
哈希是一个键值对集合,适合存储对象,如商品信息、用户信息等。比如说 value = {name: '沉默王二', age: 18}
。
集合是无序且不重复的,支持交集、并集操作,查询效率能达到 O(1)
级别,主要用于去重、标签、共同好友等场景。
有序集合的元素按分数进行排序,支持范围查询,适用于排行榜或优先级队列。
Bitmap 可以把一组二进制位紧凑地存储在一块连续内存中,每一位代表一个对象的状态,比如是否签到、是否活跃等。
Redis常用命令
Redis 支持多种数据结构,常用的命令也比较多,比如说操作字符串可以用 SET/GET/INCR
,操作哈希可以用 HSET/HGET/HGETALL
,操作列表可以用 LPUSH/LPOP/LRANGE
,操作集合可以用 SADD/SISMEMBER
,操作有序集合可以用 ZADD/ZRANGE/ZINCRBY
等,通用命令有 EXPIRE/DEL/KEYS
等。
持久化
为了保证内存中的数据不会丢失,Redis 实现了数据持久化的机制,这个机制会把数据存储到磁盘,这样在 Redis 重启就能够从磁盘中恢复原有的数据。Redis 共有三种数据持久化的方式:
- AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;
- RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘;
RDB
RDB 持久化机制可以在指定的时间间隔内将 Redis 某一时刻的数据保存到磁盘上的 RDB 文件中,当 Redis 重启时,可以通过加载这个 RDB 文件来恢复数据。
Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave,他们的区别就在于是否在「主线程」里执行:
- 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程;
- 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞
也可以在 Redis 配置文件中设置 RDB 持久化参数 save <seconds> <changes>
,表示在指定时间间隔内,如果有指定数量的键发生变化,就会自动触发 RDB 持久化。
第二种,主从复制时,当从节点第一次连接到主节点时,主节点会自动执行 bgsave 生成 RDB 文件,并将其发送给从节点
RDB 通过 fork 子进程在特定时间点对内存数据进行全量备份,生成二进制格式的快照文件。其最大优势在于备份恢复效率高,文件紧凑,恢复速度快,适合大规模数据的备份和迁移场景.
缺点是可能丢失两次快照期间的所有数据变更。
AOF 会记录每一条修改数据的写命令。这种日志追加的方式让 AOF 能够提供接近实时的数据备份,数据丢失风险可以控制在 1 秒内甚至完全避免。
缺点是文件体积较大,恢复速度慢。
AOF
Redis 在执行完一条写操作命令后,就会把该命令以追加的方式写入到一个文件里,然后 Redis 重启时,会读取该文件记录的命令,然后逐一执行命令的方式来进行数据恢复。
AOF刷盘策略
- 写回硬盘的策略, 在 Redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填:
- Always,这个单词的意思是「总是」,所以它的意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
- Everysec,这个单词的意思是「每秒」,所以它的意思是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
- No,意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘
AOF重写
由于 AOF 文件会随着写操作的增加而不断增长,为了解决这个问题, Redis 提供了重写机制来对 AOF 文件进行压缩和优化
第二种是在 Redis 配置文件中设置自动重写参数,比如说 auto-aof-rewrite-percentage
和 auto-aof-rewrite-min-size
,表示当 AOF 文件大小超过指定值时,自动触发重写。1
2auto-aof-rewrite-percentage 100 # 默认值100,表示当前AOF文件大小相比上次重写后大小增长了多少百分比时触发重写
auto-aof-rewrite-min-size 64mb # 默认值64MB,表示AOF文件至少要达到这个大小才会考虑重写
Redis 在收到重写指令后,会创建一个子进程,并 fork 一份与父进程完全相同的数据副本,然后遍历内存中的所有键值对,生成重建它们所需的最少命令。比如说多个 RPUSH 命令可以合并为一个带有多个参数的 RPUSH;
比如说一个键被设置后又被删除,这个键的所有操作都不会被写入新 AOF。
比如说使用 SADD key member1 member2 member3
代替多个单独的 SADD key memberX
。
子进程在执行 AOF 重写的同时,主进程可以继续处理来自客户端的命令。
为了保证数据一致性,Redis 使用了 AOF 重写缓冲区机制,主进程在执行写操作时,会将命令同时写入旧的 AOF 文件和重写缓冲区。
等子进程完成重写后,会向主进程发送一个信号,主进程收到后将重写缓冲区中的命令追加到新的 AOF 文件中,然后调用操作系统的 rename,将旧的 AOF 文件替换为新的 AOF 文件。
AOF 文件存储的是什么类型的数据?
AOF 文件存储的是 Redis 服务器接收到的写命令数据,以 Redis 协议格式保存。
这种格式的特点是,每个命令以*开头,后跟参数的数量,每个参数前用$
符号,后跟参数字节长度,然后是参数的实际内容。
混合持久化
混合持久化工作在 AOF 日志重写过程,当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。
配置文件中设置 aof-use-rdb-preamble yes
高可用
主从复制
主从复制允许从节点维护主节点的数据副本。在这种架构中,一个主节点可以连接多个从节点,从而形成一主多从的结构。主节点负责处理写操作,从节点自动同步主节点的数据变更,并处理读请求,从而实现读写分离。
主从复制作用
第一,主节点负责处理写请求,从节点负责处理读请求,从而实现读写分离,减轻主节点压力的同时提升系统的并发能力
第二,从节点可以作为主节点的数据备份,当主节点发生故障时,可以快速将从节点提升为新的主节点,从而保证系统的高可用性。
主从复制数据不一致性
Redis 的主从复制是异步进行的,因此在主节点宕机、网络波动或复制延迟较高时会出现从节点数据不同步的情况。比如主节点写入数据后宕机,但从节点还未来得及复制,就会出现数据不一致。另一个容易被忽视的因素是主节点内存压力。当主节点内存接近上限并启用了淘汰策略时,某些键可能被自动删除,而这些删除操作如果未能及时同步,就会造成从节点保留了主节点已经不存在的数据。
主从复制不一致的解决方案
首先是网络层面的优化,理想情况下,主从节点应该部署在同一个网络区域内,避免跨区域的网络延迟。
其次是配置层面的调整,比如说适当增大复制积压缓冲区的大小和存活时间,以便从节点重连后进行增量同步而不是全量同步,以最大程度减少主从同步的延迟。
第三是引入监控和自动修复机制,定期检查主从节点的数据一致性。
比如说通过比较主从的 offset 差值判断从库是否落后。一旦超过设定阈值,就将从节点剔除,并重新进行全量同步。