Redis - 基础知识万字总结!

Redis 是一款高性能的键值对数据库,支持多种数据结构和持久化方式。本文详细介绍了Redis的事务特性,包括原子性、生命周期、错误处理,以及RDB和AOF两种持久化策略的对比。同时,还讨论了Redis的线程模型和缓存策略,如如何处理缓存穿透、击穿和雪崩问题,以及内存淘汰策略。最后,简述了Redis的主从复制、哨兵系统和集群模式,以及与Memcached的区别。
摘要由CSDN通过智能技术生成

Redis

简介

Redis 缓存中间件。

支持的数据结构:

StringHashListSetSortedSet

HyperLogLog、Geo、Pub/Sub

事务

生命周期

  • 开启事务:使用MULTI开启一个事务

  • 命令入队列:每次操作的命令都会加入到一个队列中,但命令此时不会真正被执行

  • 提交事务:使用EXEC命令提交事务,开始顺序执行队列中的命令

事务的原子性

先看关系型数据库ACID 中关于原子性的定义:

原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

官方文档对事务的定义:

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。EXEC 命令负责触发并执行事务中的所有命令:如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。另一方面,如果客户端成功在开启事务之后执行 EXEC ,那么事务中的所有命令都会被执行。

官方认为Redis事务是一个原子操作,这是站在执行与否的角度考虑的。但是从ACID原子性定义来看,严格意义上讲Redis事务是非原子型的,因为在命令顺序执行过程中,一旦发生命令执行错误Redis是不会停止执行然后回滚数据。

Redis为什么不支持回滚

在事务运行期间虽然Redis命令可能会执行失败,但是Redis依然会执行事务内剩余的命令而不会执行回滚操作。如果你熟悉mysql关系型数据库事务,你会对此非常疑惑,Redis官方的理由如下:

只有当被调用的Redis命令有语法错误时,这条命令才会执行失败(在将这个命令放入事务队列期间,Redis能够发现此类问题),或者对某个键执行不符合其数据类型的操作:实际上,这就意味着只有程序错误才会导致Redis命令执行失败,这种错误很有可能在程序开发期间发现,一般很少在生产环境发现。 支持事务回滚能力会导致设计复杂,这与Redis的初衷相违背,Redis的设计目标是功能简化及确保更快的运行速度。

对于官方的这种理由有一个普遍的反对观点:程序有bug怎么办?但其实回归不能解决程序的bug,比如某位粗心的程序员计划更新键A,实际上最后更新了键B,回滚机制是没法解决这种人为错误的。正因为这种人为的错误不太可能进入生产系统,所以官方在设计Redis时选用更加简单和快速的方法,没有实现回滚的机制。

事务失败处理

  • 在事务提交之前:客户端执行的命令缓存(队列)失败,比如命令的语法错误(命令参数个数错误,不支持的命令等等)。如果发生这种类型的错误,Redis将向客户端返回包含错误提示信息的响应,同时Redis会清空队列中的命令并取消事务

  • 事务提交后:开始顺序执行命令,之前缓存在队列中的命令有可能执行失败。

  • 乐观锁失败事务提交时将丢弃之前缓存的所有命令序列

事务相关命令

  • WATCH

    可以为Redis事务提供 check-and-set (CAS)行为。被WATCH的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回nil来表示事务已经失败。

  • MULTI

    用于开启一个事务,它总是返回OK。MULTI执行之后,客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行,而是被放到一个队列中,当 EXEC命令被调用时, 所有队列中的命令才会被执行。

  • UNWATCH

    取消 WATCH 命令对所有 key 的监视,一般用于DISCARD和EXEC命令之前。如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。因为 EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD 命令在取消事务的同时也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行 UNWATCH 了。

  • DISCARD

    当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空,并且客户端会从事务状态中退出。

  • EXEC

    负责触发并执行事务中的所有命令。如果客户端成功开启事务后执行EXEC,那么事务中的所有命令都会被执行。

持久化

Persistence - 持久化,持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。

  • RDB持久化:能够在指定的时间间隔能对你的数据进行快照存储。

  • AOF持久化:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。

  • 不使用持久化:如果你只希望你的数据在服务器运行的时候存在,你也可以选择不使用任何持久化方式。

  • 同时开启RDB和AOF:你也可以同时开启两种持久化方式,在这种情况下当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。

RDB持久化

RDB - Redis Database,持久化是把当前内存数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发自动触发

  • 手动触发

    手动触发对应save命令,会阻塞当前Redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。

  • 自动触发

    自动触发对应bgsave命令,Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。

    在redis.conf配置文件中可以配置:

     save <seconds> <changes>

    表示xx秒内数据修改xx次时自动触发bgsave。 如果想关闭自动触发,可以在save命令后面加一个空串,即:

     save ""

    还有其他常见可以触发bgsave,如:

    • 如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点。

    • 默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则 自动执行bgsave。

    bgsave工作机制

 

  1. 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进 程,如RDB/AOF子进程,如果存在,bgsave命令直接返回。

  2. 父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,通 过info stats命令查看latest_fork_usec选项,可以获取最近一个fork操作的耗时,单位为微秒

  3. 父进程fork完成后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,可以继续响应其他命令。

  4. 子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行lastsave命令可以获取最后一次生成RDB的 时间,对应info统计的rdb_last_save_time选项。

  5. 进程发送信号给父进程表示完成,父进程更新统计信息,具体见 info Persistence下的rdb_*相关选项。

AOF持久化

AOF - append only file,持久化:以独立日志的方式记录每次写命令, 重启时再重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。

工作机制

开启AOF功能需要配置:appendonly yes,默认不开启。

AOF文件名 通过appendfilename配置设置,默认文件名是appendonly.aof。保存路径同 RDB持久化方式一致,通过dir配置指定。

AOF的工作流程操作:命令写入-append、文件同步-sync、文件重写-rewrite、重启加载-load

 

  1. 所有的写入命令会追加到aof_buf(缓冲区)中。

    AOF为什么把命令追加到aof_buf中?

    Redis使用单线程响应命令,如果每次写AOF文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载。先写入缓冲区aof_buf中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。

  2. AOF缓冲区根据对应的策略向硬盘做同步操作。

  3. 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

  4. 当Redis服务器重启时,可以加载AOF文件进行数据恢复。

AOF重写(rewrite)机制

目的

  • 减小AOF文件占用空间;

  • 更小的AOF 文件可以更快地被Redis加载恢复。

重写触发分类

  • 手动触发:直接调用bgrewriteaof命令。

  • 自动触发:根据auto-aof-rewrite-min-sizeauto-aof-rewrite-percentage参数确定自动触发时机。

    auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认 为64MB。

    auto-aof-rewrite-percentage:代表当前AOF文件空间 (aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值。

自动触发时机

aof_current_size > auto-aof-rewrite-minsize 并且(aof_current_size - aof_base_size)/ aof_base_size >= auto-aof-rewritepercentage

其中aof_current_sizeaof_base_size可以在info Persistence统计信息中查看。

 

AOF文件重写后为什么会变小?

  1. 旧的AOF文件含有无效的命令,如:del key1, hdel key2等。重写只保留最终数据的写入命令。

  2. 多条命令可以合并,如lpush list a,lpush list b,lpush list c可以直接转化为lpush list a b c。

数据恢复流程

 

  1. AOF持久化开启且存在AOF文件时,优先加载AOF文件。

  2. AOF关闭或者AOF文件不存在时,加载RDB文件。

  3. 加载AOF/RDB文件成功后,Redis启动成功。

  4. AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。

RDB与AOF比较

RDB优点

  • RDB 是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。

  • RDB 是一个紧凑的单一文件,很方便传送到另一个远端数据中心,非常适用于灾难恢复。

  • RDB 在保存 RDB 文件时父进程唯一需要做的就是 fork 出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他 IO 操作,所以 RDB 持久化方式可以最大化 Redis 的性能。

  • 与AOF相比,在恢复大的数据集的时候,RDB 方式会更快一些。

AOF优点

  • 你可以使用不同的 fsync 策略:无 fsync、每秒 fsync 、每次写的时候 fsync .使用默认的每秒 fsync 策略, Redis 的性能依然很好( fsync 是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据。

  • AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题。

  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。

  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

RDB缺点

  • Redis 要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在 Redis 意外宕机,你可能会丢失几分钟的数据。

  • RDB 需要经常 fork 子进程来保存数据集到硬盘上,当数据集比较大的时候, fork 的过程是非常耗时的,可能会导致 Redis 在一些毫秒级内不能响应客户端的请求。

AOF缺点

  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。

  • 数据恢复(load)时AOF比RDB慢,通常RDB 可以提供更有保证的最大延迟时间。

线程模型

redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。以下是单线程模式效率仍然高的原因:

  • 纯内存操作

  • 核心是基于非阻塞的 IO 多路复用机制

  • 单线程反而避免了多线程的频繁上下文切换问题

缓存XX

  • 缓存穿透

    指用户请求的数据在缓存中不存在,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时间大量请求落在数据库上,造成数据库压力过大,甚至击垮数据库系统。

    解决方案

    1. 布隆过滤器 - Bloom Filter

      布隆过滤器专门用来检测集合中是否存在特定的元素

      设计思想

      布隆过滤器由一个长度为m比特的位数组(bit array)与k个哈希函数(hash function)组成的数据结构。位数组初始化均为0,所有的哈希函数都可以分别把输入数据尽量均匀地散列。

      当要向布隆过滤器中插入一个元素时,该元素经过k个哈希函数计算产生k个哈希值,以哈希值作为位数组中的下标,将所有k个对应的比特值由0置为1。

      当要查询一个元素时,同样将其经过哈希函数计算产生哈希值,然后检查对应的k个比特值:如果有任意一个比特为0,表明该元素一定不在集合中;如果所有比特均为1,表明该集合有可能性在集合中。为什么不是一定在集合中呢?因为不同的元素计算的哈希值有可能一样,会出现哈希碰撞,导致一个不存在的元素有可能对应的比特位为1,这就是所谓“假阳性”(false positive)。相对地,“假阴性”(false negative)在BF中是绝不会出现的。

      优点

      • 节省空间

        不需要存储数据本身,只需要存储数据对应hash比特位

      • 时间复杂度低

        插入和查找的时间复杂度都为O(k),k为哈希函数的个数

      缺点

      • 存在假阳性

        布隆过滤器判断存在,可能出现元素不在集合中;判断准确率取决于哈希函数的个数

      • 不能删除元素

        如果一个元素被删除,但是却不能从布隆过滤器中删除,这也是造成假阳性的原因了

    2. 返回空对象

      当缓存未命中,查询持久层也为空,可以将返回的空对象写到缓存中,这样下次请求该key时直接从缓存中查询返回空对象,请求不会落到持久层数据库。为了避免存储过多空对象,通常会给空对象设置一个过期时间。这种方法会存在两个问题:

      • 如果有大量的key穿透,缓存空对象会占用宝贵的内存空间。

      • 空对象的key设置了过期时间,在这段时间可能会存在缓存和持久层数据不一致的场景。

  • 缓存击穿

    指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,数据库瞬时压力骤增,造成大量请求阻塞。

    解决方案

    1. 互斥锁 - mutex key

      如果是分布式应用就需要使用分布式锁。

      让一个线程回写缓存,其他线程等待回写缓存线程执行完,重新读缓存即可。同一时间只有一个线程读数据库然后回写缓存,其他线程都处于阻塞状态。如果是高并发场景,大量线程阻塞势必会降低吞吐量。

    2. 热点数据永不过期

      永不过期实际包含两层意思:

      • 物理不过期

        针对热点key不设置过期时间

      • 逻辑不过期

        把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建

      从实战看这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,对于不追求严格强一致性的系统是可以接受的。

  • 缓存雪崩

    缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,请求直接落到数据库上,引起数据库压力过大甚至宕机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是h很多数据都过期了,很多数据都查不到从而查数据库。

    解决方案

    1. 均匀过期

      设置不同的过期时间,让缓存失效的时间点尽量均匀。通常可以为有效期增加随机值或者统一规划有效期。

    2. 加互斥锁

      跟缓存击穿解决思路一致,同一时间只让一个线程构建缓存,其他线程阻塞排队。

    3. 缓存永不过期

      跟缓存击穿解决思路一致,缓存在物理上永远不过期,用一个异步的线程更新缓存。

    4. 双层缓存策略

      使用主备两层缓存

      主缓存:有效期按照经验值设置,设置为主读取的缓存,主缓存失效后从数据库加载最新值。

      备份缓存:有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存。

  • 缓存预热

    系统上线后,将相关的缓存数据直接加载到缓存系统,这样就可以避免在用户请求的时候,先查询数据库,然后再将数据回写到缓存。如果不进行预热, 那么 Redis 初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。

    缓存预热的操作方法

    • 数据量不大的时候,工程启动的时候进行加载缓存动作;

    • 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;

    • 数据量太大的时候,优先保证热点数据进行提前加载到缓存。

  • 缓存降级

    指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。

    在项目实战中通常会将部分热点数据缓存到服务的内存中,这样一旦缓存出现异常,可以直接使用服务的内存数据,从而避免数据库遭受巨大压力。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。

内存淘汰策略

内存淘汰策略是指当缓存内存不足时,通过淘汰旧数据处理新加入数据选择的策略。Redis最大占用内存用完之后如果继续添加数据,有以下八种处理策略。

  1. noeviction

    默认策略,对于写请求直接返回错误,不进行淘汰。

  2. allkeys-lru

    lru(less recently used), 最近最少使用。从所有的key中使用近似LRU算法进行淘汰。

  3. volatile-lru

    最近最少使用。从设置了过期时间的key中使用近似LRU算法进行淘汰。

  4. allkeys-random

    从所有的key中随机淘汰。

  5. volatile-random

    从设置了过期时间的key中随机淘汰。

  6. volatile-ttl

    ttl(time to live),在设置了过期时间的key中根据key的过期时间进行淘汰,越早过期的越优先被淘汰。

  7. allkeys-lfu

    lfu(Least Frequently Used),最少使用频率。从所有的key中使用近似LFU算法进行淘汰。从Redis4.0开始支持。

  8. volatile-lfu

    最少使用频率。从设置了过期时间的key中使用近似LFU算法进行淘汰。从Redis4.0开始支持。

    当使用volatile-lruvolatile-randomvolatile-ttl这三种策略时,如果没有设置过期的key可以被淘汰,则和noeviction一样返回错误。

淘汰算法

LRU在Redis中的实现

LRU(Least Recently Used),即最近最少使用,是一种缓存置换算法。在使用内存作为缓存的时候,缓存的大小一般是固定的。当缓存被占满,这个时候继续往缓存里面添加数据,就需要淘汰一部分老的数据,释放内存空间用来存储新的数据。这个时候就可以使用LRU算法了。其核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉。

Redis使用的是近似LRU算法,它跟常规的LRU算法还不太一样。近似LRU算法通过随机采样法淘汰数据,每次随机出5个(默认)key,从里面淘汰掉最近最少使用的key。可以通过maxmemory-samples参数修改采样数量, 如:maxmemory-samples 10maxmenory-samples配置的越大,淘汰的结果越接近于严格的LRU算法,但因此耗费的CPU也很高。Redis为了实现近似LRU算法,给每个key增加了一个额外增加了一个24bit的字段,用来存储该key最后一次被访问的时间

Redis3.0对近似LRU的优化

Redis3.0对近似LRU算法进行了一些优化。新算法会维护一个候选池(大小为16),池中的数据根据访问时间进行排序,第一次随机选取的key都会放入池中,随后每次随机选取的key只有在访问时间小于池中最小的时间才会放入池中,直到候选池被放满。当放满后,如果有新的key需要放入,则将池中最后访问时间最大(最近被访问)的移除。当需要淘汰的时候,则直接从池中选取最近访问时间最小(最久没被访问)的key淘汰掉就行。

LFU算法

LFU(Least Frequently Used),是Redis4.0新加的一种淘汰策略,它的核心思想是根据key的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。LFU算法能更好的表示一个key被访问的热度。假如你使用的是LRU算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key将来是很有可能被访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key成为热点数据。

主从复制

指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。

主从复制的作用

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。

  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。

  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。

  • 高可用基石:主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

主从复制实现原理

  1. 连接建立

    1. 保存主节点信息

      slaveof命令是异步的,在从节点上执行slaveof命令,从节点立即向客户端返回ok,从节点服务器内部维护了两个字段,即masterhostmasterport字段,用于存储主节点的ip和port信息。

    2. 建立socket连接

      从节点每秒1次调用复制定时函数replicationCron(),如果发现了有主节点可以连接,便会根据主节点的ipport,创建socket连接。从节点为该socket建立一个专门处理复制工作的文件事件处理器,负责后续的复制工作,如接收RDB文件、接收命令传播等。

      主节点接收到从节点的socket连接后(即accept之后),为该socket创建相应的客户端状态,并将从节点看做是连接到主节点的一个客户端,后面的步骤会以从节点向主节点发送命令请求的形式来进行。

    1. 发送ping命令

      从节点成为主节点的客户端之后,发送ping命令进行首次请求,目的是:检查socket连接是否可用,以及主节点当前是否能够处理请求。从节点发送ping命令后,可能出现3种情况:

      • 返回pong:说明socket连接正常,且主节点当前可以处理请求,复制过程继续。

      • 超时:一定时间后从节点仍未收到主节点的回复,说明socket连接不可用,则从节点断开socket连接,并重连。

      • 返回pong以外的结果:如果主节点返回其他结果,如正在处理超时运行的脚本,说明主节点当前无法处理命令,则从节点断开socket连接,并重连。

    2. 身份验证

      如果从节点中设置了masterauth选项,则从节点需要向主节点进行身份验证;没有设置该选项,则不需要验证。从节点进行身份验证是通过向主节点发送auth命令进行的,auth命令的参数即为配置文件中的masterauth的值。

      如果主节点设置密码的状态,与从节点masterauth的状态一致(一致是指都存在,且密码相同,或者都不存在),则身份验证通过,复制过程继续;如果不一致,则从节点断开socket连接,并重连。

    3. 发送从节点端口信息

      身份验证之后,从节点会向主节点发送其监听的端口号(前述例子中为6380),主节点将该信息保存到该从节点对应的客户端的slave_listening_port字段中;该端口信息除了在主节点中执行info Replication时显示以外,没有其他作用。

Sentinel - 哨兵模式

Redis 的主从复制模式下,一旦主节点由于故障不能提供服务,需要手动将从节点晋升为主节点,同时还要通知客户端更新主节点地址。Redis 2.8 以后提供了 Redis Sentinel 哨兵机制来解决这个问题。Redis Sentinel 是 Redis 高可用的实现方案。Sentinel 是一个管理多个 Redis 实例的工具,它可以实现对 Redis 的监控、通知、自动故障转移。

哨兵模式的原理

哨兵模式的主要作用在于它能够自动完成故障发现和故障转移,并通知客户端,从而实现高可用。哨兵模式通常由一组 Sentinel 节点和一组(或多组)主从复制节点组成。

心跳机制

  1. Sentinel与Redis Node

    Redis Sentinel 是一个特殊的 Redis 节点。在哨兵模式创建时,需要通过配置指定 Sentinel 与 Redis Master Node 之间的关系,然后 Sentinel 会从主节点上获取所有从节点的信息,之后 Sentinel 会定时向主节点和从节点发送 info 命令获取其拓扑结构和状态信息。

  1. Sentinel与Sentinel

    基于 Redis 的订阅发布功能, 每个 Sentinel 节点会向主节点的 sentinel:hello 频道上发送该 Sentinel 节点对于主节点的判断以及当前 Sentinel 节点的信息 ,同时每个 Sentinel 节点也会订阅该频道, 来获取其他 Sentinel 节点的信息以及它们对主节点的判断。

    通过以上两步所有的 Sentinel 节点以及它们与所有的 Redis 节点之间都已经彼此感知到,之后每个 Sentinel 节点会向主节点、从节点、以及其余 Sentinel 节点定时发送 ping 命令作为心跳检测, 来确认这些节点是否可达。

故障转移

每个 Sentinel 都会定时进行心跳检查,当发现主节点出现心跳检测超时的情况时,此时认为该主节点已经不可用,这种判定称为主观下线。之后该 Sentinel 节点会通过 sentinel ismaster-down-by-addr 命令向其他 Sentinel 节点询问对主节点的判断, 当 quorum(法定人数) 个 Sentinel 节点都认为该节点故障时,则执行客观下线,即认为该节点已经不可用。这也同时解释了为什么必须需要一组 Sentinel 节点,因为单个 Sentinel 节点很容易对故障状态做出误判。

这里 quorum 的值是我们在哨兵模式搭建时指定的,后文会有说明,通常为 Sentinel节点总数/2+1即半数以上节点做出主观下线判断就可以执行客观下线

因为故障转移的工作只需要一个 Sentinel 节点来完成,所以 Sentinel 节点之间会再做一次选举工作, 基于 Raft 算法选出一个 Sentinel 领导者来进行故障转移的工作。被选举出的 Sentinel 领导者进行故障转移的具体步骤如下:

  1. 在从节点列表中选出一个节点作为新的主节点

    • 过滤不健康或者不满足要求的节点;

    • 选择 slave-priority(优先级)最高的从节点, 如果存在则返回, 不存在则继续;

    • 选择复制偏移量最大的从节点 , 如果存在则返回, 不存在则继续;

    • 选择 runid 最小的从节点。

  2. Sentinel 领导者节点会对选出来的从节点执行 slaveof no one 命令让其成为主节点。

  3. Sentinel 领导者节点会向剩余的从节点发送命令,让他们从新的主节点上复制数据。

  4. Sentinel 领导者会将原来的主节点更新为从节点, 并对其进行监控, 当其恢复后命令它去复制新的主节点。

Cluster - 集群

引入Cluster模式的原因: 不管是主从模式还是哨兵模式都只能由一个master在写数据,在海量数据高并发场景,一个节点写数据容易出现瓶颈,引入Cluster模式可以实现多个节点同时写数据。

Redis-Cluster 采用无中心结构,每个节点都保存数据,节点之间互相连接从而知道整个集群状态。

如图所示Cluster模式其实就是多个主从复制的结构组合起来的,每一个主从复制结构可以看成一个节点,那么上面的Cluster集群中就有三个节点。

与Memcache的区别

  • 存储方式

    Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。

    Redis有部份存在硬盘上,这样能保证数据的持久性。

  • 数据支持类型

    Memcache对数据类型支持相对简单。

    Redis有丰富的数据类型。

  • 使用底层模型不同

    它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。

    Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值