Redis基本概念

什么是Redis

在日常的开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题,可是一旦涉及大数据量的需求,比如一些商品抢购的情景,或者是主页访问量瞬间较大的时候,单一使用数据库来保存数据的系统会因为面向磁盘,而磁盘读写速度比较慢的问题而存在严重的性能弊端,一瞬间成千上万的请求到来,需要系统在极短的时间内完成成千上万次的读/写操作,这个时候往往不是数据库能够承受的,极其容易造成数据库系统瘫痪,最终导致服务宕机的严重生产问题。为了克服上述的问题,项目通常会引入NoSQL技术,这是一种基于内存的数据库,并且提供一定的持久化功能。


NoSQL,指的是非关系型的数据库。NoSQL也可被认为是Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。

对NoSQL最普遍的解释是"非关系型的",强调Key-Value Stores和文档数据库的优点,并不是单纯的反对传统关系型数据库,而是作为补充,一般用于超大规模数据的存储。


关系型数据库与非关系型数据库的区别

关系型数据库:MySQL是关系型数据库中的一种。以表的形式存储数据。

非关系型数据库:Redis是属于非关系型数据库中的一种。以键值对的形式存储。


Redis (全称Remote Dictionary Service,即远程字典服务器)是一个开源的,C语言编写的高性能数据结构存储系统,它可以用作数据库、缓存和消息中间件。

它是基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSQL数据库之一。

MySQL的数据存在磁盘中,Redis的数据是存在内存中的,因此具有极快的读写速度。

Redis官网

Redis中文学习网

主要特点

  1. 内存存储:数据主要存储在内存中,提供毫秒级的响应时间

  2. 数据持久化:支持RDB快照和AOF日志两种持久化方式

  3. 丰富的数据类型:支持字符串、哈希、列表、集合、有序集合等多种数据结构

  4. 高可用性:支持主从复制、哨兵模式和集群模式

  5. 原子操作:所有操作都是原子性的

常用命令

Redis默认16个数据库,编号0-15

常用五大数据类型

Redis中存储的数据都是以键值对的形式存在。key一定是字符串,value的类型则有很多。

Redis支持多种数据类型,其中最常用的五大数据类型分别是:String(字符串)、Hash(哈希)、List(列表)、Set(集合)和Sorted Set(有序集合)。

String(字符串)

常用命令:

Hash(哈希)

常用命令:

List(列表)

常用命令:

Set(集合)

常用命令:

Sorted Set(有序集合)

常用命令:

持久化

概念

redis属于内存数据库,数据都存放在内存中,内存具备易失性,如果服务器异常断开或者重启之后,数据就会丢失。

为了防止数据的丢失,需要定期的将数据从内存保存到磁盘,这就是持久化。

Redis有两种持久化方式

RDB持久化

RDB持久化的概念

RDB(Redis Database)是 Redis 的一种持久化方式,它通过创建数据快照的方式将内存中的数据保存到磁盘上的二进制文件中。RDB 文件是一个经过压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。

RDB是Redis 默认的持久化方案(而AOF默认是关闭的)。

在指定的时间间隔内,执行指定次数的写操作,则会将Redis在内存中的数据写入到磁盘中,即在指定目录下生成一个dump.rdb文件(快照文件)。

Redis重启会通过加载dump.rdb文件恢复数据。(/var/lib/redis/6379)

快照生成过程

  1. Redis 主进程 fork() 创建子进程

  2. 子进程将内存数据写入临时 RDB 文件

  3. 写入完成后,用临时文件替换旧的 RDB 文件

  4. 子进程退出,主进程继续处理客户端请求

Copy-on-Write (COW) 机制

触发方式

自动触发

在指定的时间间隔内,执行指定次数的写操作,就会自动触发。

默认在3600秒内写1次,300秒内写100次,或者60秒内写10000次,就会触发快照。

Note

是在指定的时间间隔内,要执行指定次数,这样两个条件都满足的情况下,才能触发快照。

通过配置文件设置自动保存条件:

手动触发

SAVE 命令(同步)

BGSAVE 命令(异步)

SHUTDOWN 命令

flushall 命令

RDB文件格式

  1. REDIS:文件标识符(5字节)

  2. RDB-VERSION:RDB 版本号(4字节)

  3. SELECT-DB:数据库选择器

  4. KEY-VALUE-PAIRS:键值对数据

  5. EOF:文件结束标志

  6. CHECK-SUM:校验和(8字节)

RDB的优缺点

优点

当数据量比较大,也就是大规模数据的恢复时,建议使用RDB持久化(相对于AOF要快一些)。

换言之,若数据很多(说明写操作很多),把数据从磁盘复制到内存比重新执行所有的写操作更快。

如果对数据的完整性与一致性要求不高,可以使用RDB(快)

Tip

默认的60秒内10000次写操作触发快照,如果58秒进行了10000次写操作然后系统突然崩溃了,那么Redis还没有来得及触发快照操作来将这10000次写操作后的最新数据保存到RDB文件中。所以,从上次成功生成RDB快照之后到系统崩溃前的这10000次写操作的数据就会丢失。而AOF默认是1秒钟就触发一次。

  1. 性能高效:子进程处理,不影响主进程性能

  2. 文件紧凑:二进制格式,文件小,传输快

  3. 恢复快速:直接加载到内存,启动速度快

  4. 适合备份:定期生成完整数据快照

  5. 适合灾难恢复:可以轻松复制到远程服务器

缺点

  1. 数据丢失风险:数据的完整性与一致性不高,数据丢失可能比较多

  2. fork 开销:在使用BGSAVE指令时,父进程会fork出一个子进程,让子进程进行持久化,所以占用内存

  3. 内存占用:COW 机制可能导致内存使用翻倍

  4. 不适合实时性:无法做到秒级数据持久化

AOF持久化

概念

AOF(Append Only File)是Redis的一种持久化方式,通过记录每个写操作命令来保证数据的持久性。就是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍。


AOF提供三种同步策略:

always:每个写命令都立即同步到磁盘

everysec(默认):每秒同步一次

no:由操作系统决定何时同步

AOF重写机制

Redis可以在不打断服务客户端的情况下,对 AOF 文件进行重写,或者称作重建(rebuild)。

其目的在于减小AOF文件大小、提高数据恢复速度和移除冗余命令。

可以人为地执行BGREWRITEAOF命令,Redis将生成一个新的AOF文件(备份文件),这个文件包含重建当前数据集所需的最少命令。

Redis进行AOF持久化时也会自动触发重写,但要满足一定条件:

Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后AOF文件大小的两倍(增幅100%)且文件大于64M时触发

触发条件


重写过程

  1. fork子进程:创建子进程进行重写,避免阻塞主进程

  2. 生成新AOF文件:遍历当前数据库状态,生成最少的命令集合

  3. 处理重写期间的新命令:主进程将新命令同时写入AOF缓冲区和AOF重写缓冲区

  4. 替换旧文件:重写完成后,将重写缓冲区的命令追加到新文件,然后替换旧AOF文件

AOF的优缺点

优点

  1. 数据安全性高:可以配置为每个命令都同步

  2. 文件可读性好:纯文本格式,便于理解和修复

  3. 支持增量备份:可以实时备份AOF文件

  4. 恢复精度高:可以精确到某个时间点

缺点

  1. 文件体积大:如果一直达不到重写条件,AOF文件的大小会比较大

  2. 恢复速度慢:需要重放所有命令

  3. 性能开销:写操作需要额外的磁盘I/O

Redis事务

概念

Redis事务是一组命令的集合,可以一次执行多个命令,这些命令会被顺序执行,并且在执行过程中不会被其他客户端的命令打断。

一个事务中的所有命令都会序列化,按顺序的串行化执行,而不会被其他命令插入,不许加塞。

一个事务从开始到执行会经历以下三个阶段开始事务、 命令入队、执行事务。

特点

基本命令

  1. MULTI,开启事务,后续命令将被放入队列中

  2. EXEC,执行事务队列中的所有命令

  3. DISCARD,取消事务,清空命令队列

  4. WATCH, 监视一个或多个键,如果在事务执行前这些键被修改,事务将被取消

  5. UNWATCH, 取消对所有键的监视

执行流程

基本流程


带监视的事务

Note

一旦执行EXEC,WATCH监控会被取消。

如果想要取消监控,在开始事务之前可以使用UNWATCH命令。

UNWATCH 指令是原子操作,它会立即取消所有监控,并且不会被其他客户端的操作打断。

不要在命令入队的阶段使用UNWATCH,因为事务开始后输入的命令都会在EXEC时才执行。

Redis事务有以下三个重要的保证:

  1. 批量操作在发送 EXEC 命令前被放入队列缓存。

  2. 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。

  3. 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中(也就是其他客户端仍然正常使用,不用等待第一个客户端的事务执行完)。

错误处理

1. 语法错误(编译时错误)

如果事务队列中有语法错误的命令,整个事务都不会执行:

2. 运行时错误

如果命令语法正确但执行时出错,其他命令仍会继续执行:

Note

Redis的事务是不是原子的?

原子性是一个数据库和并发编程中的重要概念,它描述的是一个操作序列要么全部执行成功,要么全部不执行,不存在部分执行的中间状态。就像一个不可分割的原子一样,操作在执行过程中不会被其他操作中断或干扰。

Redis事务中的前一条命令执行失败,对后面的命令没有影响,看起来Redis的事务不具备原子性。

但如果在事务中包含着错误的指令,整个事务都不会执行,从这个角度看,redis又表现出一定的原子性。

因此,Redis事务在入队错误时具备原子性,但在执行时错误时不具备严格意义上的原子性。

严格来说,redis中的事务是没有原子性的。

事务的限制

1. 不支持回滚

2. 无法在事务中使用条件判断

3. WATCH的限制

乐观锁与悲观锁

乐观锁

底层的机制:CAS版本号机制

Tip

CAS机制:

会将变量的预期值A与在内存中的值V进行比较,如果V与A是相等,就可以将该值改为B;如果V与A的值是不相等,那么就不能将其改为新值B。


版本号机制:

一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加1。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。(如果读取到的version值为当前数据库中的version值不相等,说明有其他线程修改了数据

悲观锁

在多线程环境下,对于共享资源而言,每个线程访问数据的时候,都认为其他的线程会修改数据,所以每次在拿数据的时候都会上锁。当其他线程想要访问数据时,都需要阻塞挂起。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁,读锁,写锁等,都是在操作之前先上锁。

主从复制

基本概念

冷备份与热备份

冷备份也称为静态备份,是指在Redis服务器停止运行的状态下进行数据备份。操作方式通常是直接拷贝Redis的数据文件(如RDB文件或AOF文件)到其他存储介质或位置。例如,可以使用操作系统的复制命令将RDB文件复制到远程服务器或本地的另一个磁盘分区。

优点

缺点


热备份也叫在线备份,是指在Redis服务器正常运行、对外提供服务的情况下进行数据备份。一种重要的手段就是主从复制模式。

主从复制

Redis的主从复制是Redis高可用架构的基础,通过将数据从主服务器(Master)复制到从服务器(Slave),实现数据备份、读写分离和故障恢复。

数据的复制是单向的,只能由主节点到从节点。

复制过程

第一阶段:建立连接

第二阶段:数据同步

  1. 全量同步(Full Resynchronization)

    • 从服务器发送PSYNC命令

    • 主服务器执行BGSAVE生成RDB文件

    • 主服务器将RDB文件发送给从服务器

    • 从服务器清空数据库并载入RDB文件

    • 主服务器将缓冲区中的写命令发送给从服务器

  2. 增量同步(Partial Resynchronization)

    • 主服务器维护复制偏移量和复制积压缓冲区

    • 从服务器断线重连后发送PSYNC命令

    • 如果偏移量在缓冲区范围内,执行增量同步

    • 否则执行全量同步

配置主从复制

主服务器配置

从服务器配置

动态配置

复制的关键机制

复制偏移量(Replication Offset)

复制积压缓冲区(Replication Backlog)

服务器运行ID(Run ID)

复制的类型

全量复制

触发条件:

过程:

增量复制

触发条件:

过程:

主从复制的优缺点

优点

  1. 数据备份:提供数据冗余,防止数据丢失

  2. 读写分离:主服务器处理写操作,从服务器处理读操作

  3. 故障恢复:主服务器故障时可以快速切换

  4. 负载均衡:分散读请求压力


缺点

  1. 数据延迟:主从之间存在数据同步延迟

  2. 写操作瓶颈:所有写操作都在主服务器上

  3. 故障切换复杂:需要手动或自动故障切换机制

  4. 资源消耗:需要额外的服务器资源

Note

1、主机每次进行了写操作,数据会复制到从机;而从机不能进行写指令,只能进行读指令

2、如果从机掉线了,主机不会再记录从机的信息,即使从机后面又上线了,主机也不会记录从机的信息,从机是以主机的身份上来的。

3、如果主机掉线了,那么从机会记录主机的信息,包括:ip、port、在线状态,但是从机依旧是从机,也就是不能写。后续如果主机上线了,那么主从复制的状态会立马恢复,也就是主机依旧是主机,从机依旧是从机。

问题:如果主机一直不上线,那么就不能执行写操作,这就是传统的主从复制的问题。

哨兵模式

基本概念

Redis主从复制有数据热备、负载均衡、故障恢复等作用,但主从复制存在的一个问题,故障恢复无法自动化

哨兵(Sentinel)模式是Redis官方提供的高可用解决方案,基于Redis主从复制,主要作用便是监控Redis主从集群,并在主服务器故障时自动进行故障转移,进一步提高系统的高可用性。

Redis哨兵系统是一个分布式系统,可以在一个架构中运行多个哨兵进程(Sentinel progress),这些进程使用流言协议(gossip protocols)来接收关于主服务器是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。

哨兵模式可以解决传统的主从复制下,当主服务器断开之后,从服务器就不能写的问题。

Tip

流言协议(哨兵之间传递消息)

投票协议(少数服从多数)

核心功能

  1. 监控(Monitoring):持续监控主从服务器是否正常工作

  2. 通知(Notification):当被监控的Redis实例出现问题时,通过API通知系统管理员或其他程序

  3. 自动故障转移(Automatic Failover):当主服务器不能正常工作时,自动将某个从服务器升级为主服务器

  4. 配置提供者(Configuration Provider):为客户端提供当前主服务器的地址

架构组成

工作原理

1. 监控

主观下线(Subjective Down, SDOWN)

客观下线(Objective Down, ODOWN)


2. 故障转移

  1. 领导者选举

    • 使用Raft算法选举领导者Sentinel

    • 获得超过半数Sentinel投票的节点成为领导者

    • 领导者负责执行故障转移

  2. 新主服务器选择规则

    • 排除下线和断线的从服务器

    • 选择优先级最高的从服务器(replica-priority)

    • 选择复制偏移量最大的从服务器

    • 选择运行ID最小的从服务器

  3. 故障转移步骤

    • 向选定的从服务器发送SLAVEOF NO ONE命令

    • 等待从服务器切换为主服务器

    • 向其他从服务器发送SLAVEOF命令

    • 更新Sentinel配置

    • 通知客户端新的主服务器地址

Note

如果只设置一个哨兵,由于流言协议的特性,可能会存在误判的情况。

比如主服务器可能遭遇短暂的硬件故障或软件异常,导致其在短时间内无法响应哨兵的 ping 命令,但很快又恢复正常。如果哨兵在主服务器故障期间没有再次发送 ping 命令或者发送间隔较长,就可能错过主服务器故障的检测,认为主服务器一直处于正常状态。

又或者当网络出现短暂的延迟、丢包等波动情况时,哨兵发送的 ping 命令可能会在网络中丢失或延迟到达主服务器,或者主服务器的响应也可能延迟到达哨兵。如果这种延迟超过了哨兵的主观下线超时时间,哨兵就会误认为主服务器下线。

所以实际使用中可以多设置几个哨兵监测同一个服务器来保证准确性和安全性。

因为多个哨兵之间可以进行相互验证,它们并非仅依赖单一来源的消息来判断主服务器状态,而是会独立地与主服务器及其他哨兵进行通信,并根据自己对主服务器的监测以及从其他多个哨兵处收集到的信息来综合判断主服务器的状态。如果有个别虚假信息传播,很难让所有哨兵都被误导,因为大多数哨兵基于自身真实监测和多数一致的信息会做出正确判断。

三个缓存问题

缓存雪崩

缓存雪崩(Cache Avalanche)是指在某一时刻,导致本来可以在缓存中查到数据,现在必须要到底层数据库下找,这样会加大对底层数据库的压力,造成数据库压力骤增甚至宕机的现象。

产生原因


解决方案

  1. 设置随机过期时间

  2. 缓存预热

  3. 多级缓存

  4. 熔断降级

缓存击穿

缓存击穿(Cache Breakdown)是指某个热点key在缓存过期的瞬间,有大量并发请求同时访问这个key,导致请求直接打到数据库的现象。

产生原因


解决方案

  1. 互斥锁

  2. 热点数据永不过期

  3. 提前异步刷新

缓存穿透

缓存穿透(Cache Penetration)是指查询一个不存在的数据,即要访问的数据在缓存以及底层数据库下都不存在,由于缓存和数据库都没有这个数据,每次请求都会穿透缓存直接查询数据库。

产生原因


解决方案

  1. 缓存空值

  2. 布隆过滤器

  3. 参数校验

  4. 限流和监控


问题类型影响范围主要原因核心解决思路
缓存雪崩大范围大量缓存同时失效避免同时失效
缓存击穿单点热点热点数据过期避免并发重建
缓存穿透持续影响查询不存在数据避免无效查询