Redis 系列

Redis 和 MySQL如何保证数据一致性


一般情况下,Redis 用来实现应用和数据库之间读操作的缓存层,主要目的是减少数据库 IO,还可以提升数据的 IO 性能。当应用程序需要去读取某个数据的时候,首先会先尝试去 Redis 里面加载,如果命中就直接返回。如果没有命中,就从数据库查询,查询到数据后再把这个数据缓存到 Redis 里面。当数据发生变化的时候,需要同时更新 Redis 和 Mysql来保证数据一致性,由于更新是有先后顺序的,并且它不像 Mysql 中的多表事务操作,可以满足 ACID 特性。所以就会出现数据一致性问题。在这种情况下,能够选择的方法只有两种

①先更新数据库,再更新缓存

②先删除缓存,再更新数据库

如果先更新数据库,再更新缓存,如果缓存更新失败,就会导致数据库和 Redis中的数据不一致。如果是先删除缓存,再更新数据库,理想情况是应用下次访问 Redis 的时候,发现 Redis 里面的数据是空的,就从数据库加载保存到 Redis 里面,那么数据是一致的。但是在极端情况下,由于删除 Redis 和更新数据库这两个操作并不是原子的,所以这个过程如果有其他线程来访问,还是会存在数据不一致问题。所以,如果需要在极端情况下仍然保证 Redis 和 Mysql 的数据一致性,就只能采用最终一致性方案。比如基于 RocketMQ 的可靠性消息通信,来实现最终一致性。还可以直接通过 Canal 组件,监控 Mysql 中 binlog 的日志,把更新后的数据同步到 Redis 里面。因为这里是基于最终一致性来实现的,如果业务场景不能接受数据的短期不一致性,那就不能使用这个方案来做。


说说基于Redis的分布式锁


Redis它里面提供了 SETNX 命令可以实现锁的排他性,当 key 不存在就返回1,同时设置 key 的值,存在就返回 0,SETNX 啥也不做,然后通过del删除锁。然后还可以用 expire 命令设置锁的失效时间,从而避免死锁问题,当然这一步需要是原子操作,可以通过lua脚本来实现。当然有可能操作共享资源的时间大于过期时间,就会出现锁提前过期的问题,进而导致分布式锁直接失效。如果锁的超时时间设置过长,又会影响到性能。所以这种情况,可以写一个定时任务对指定的 key 进行续期。Redisson 这个开源组件,就提供了分布式锁的封装实现,并且也内置了一个Watch Dog 机制来对 key 做续期。一般情况下 Redis 里面这种分布式锁设计已经能够解决 99%的问题了,当然如果在Redis 搭建了高可用集群的情况下出现主从切换导致 key 失效,这个问题也有可能造成多个线程抢占到同一个锁资源的情况,所以 Redis 官方也提供了一个 RedLock的解决办法,但是实现会相对复杂一些,Redlock 算法的思想是让客户端向 Redis 集群中的多个独立的 Redis 实例依次请求申请加锁,如果客户端能够和半数以上的实例成功地完成加锁操作,那么我们就认为,客户端成功地获得分布式锁,否则加锁失败。即使部分 Redis 节点出现问题,只要保证 Redis 集群中有半数以上的 Redis 节点可用,分布式锁服务就是正常的。

Redlock 是直接操作 Redis 节点的,并不是通过 Redis 集群操作的,这样才可以避免 Redis 集群主从切换导致的锁丢失问题。Redlock 实现比较复杂,性能比较差,而且还存在各种安全问题,实际项目中不建议使用 Redlock 算法,成本和收益不成正比。如果不是非要实现绝对可靠的分布式锁的话,其实单机版 Redis 就完全够了,实现简单,性能也非常高。如果你必须要实现一个绝对可靠的分布式锁的话,可以基于 Zookeeper 来做,只是性能会差一些。


Redis是否存在线程安全问题


首先,Redis在6.0支持的多线程,并不是说指令操作的多线程,而是针对网络IO的多线程支持。

也就是Redis的命令操作,仍然是线程安全的。其次, Redis本身的性能瓶颈,取决于三个纬度,网络、CPU、内存。而真正影响 关键问题是像内存和网络。而Redis6.0的多线程,本质上解决网络IO的处理效率问题。


Redis的持久化机制


为了避免 Redis 故障导致数据丢失的问题,Redis提供了 RDB 和 AOF 两种持久化机制。

RDB:Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本,还可以将快照留在原地以便重启服务器的时候使用。RDB 快照的触发方式有很多,比如执行 bgsave 命令触发异步快照,执行 save命令触发同步快照,同步快照会阻塞客户端的执行指令。根据 redis.conf 文件里面的配置,自动触发 bgsave 指令。

AOF:与快照持久化相比,AOF 持久化的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启,

开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到内存缓存 server.aof_buf 中,然后再根据 appendfsync 配置来决定何时将其同步到硬盘中的 AOF 文件。

AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof。

在 Redis 的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:

appendfsync always    #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec  #每秒钟同步一次,显式地将多个写命令同步到硬盘
appendfsync no        #让操作系统决定何时进行同步

为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件,Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis 还会放慢自己的速度以便适应硬盘的最大写入速度。

另外,因为 AOF 这种指令追加的方式,会造成 AOF 文件过大,带来明显的 IO性能问题,所以 Redis 针对这种情况提供了 AOF 重写机制,也就是说当 AOF文件的大小达到某个阈值的时候,就会把这个文件里面相同的指令进行压缩。

因此,基于对 RDB 和 AOF 的工作原理的理解,我认为 RDB 和 AOF 的优缺点有两个。RDB 是每隔一段时间触发持久化,因此数据安全性低,AOF 可以做到实时持久化,数据安全性较高, RDB 文件默认采用压缩的方式持久化,AOF 存储的是执行指令,所以 RDB 在数据恢复的时候性能比 AOF 要好。


谈谈对Redis的理解


首先,Redis 是一个基于 Key-Value 存储结构的 Nosql 开源内存数据库。

为了满足不同的业务场景,Redis 内置了多种数据类型实现(比如 String、Hash、Sorted Set、Bitmap)。因此它可以覆盖应用开发中大部分的业务场景,比如 top10 问题、好友关注列表、热点话题等。

其次,由于 Redis 是基于内存存储,并且在数据结构上做了大量的优化所以 IO性能比较好,在实际开发中,会把它作为应用与数据库之间的一个分布式缓存组件。并且它又是一个非关系型数据的存储,不存在表之间的关联查询问题,所以它可以很好的提升应用程序的数据 IO 效率。

最后,作为企业级开发来说,它又提供了主从复制+哨兵、以及集群方式实现高可用,在 Redis 集群里面,通过 hash 槽的方式实现了数据分片,进一步提升了性能。


Redis的数据类型以及各自的应用场景


基本数据结构:String(字符串)、List(列表)、Set(集合)、Hash(散列)、Zset(有序集合)。

①String(字符串):

Redis 中最简单同时也是最常用的一个数据结构。String 是一种二进制安全的数据结构,可以用来缓存 session、token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存),还可以用来做计数。

②List(列表):

List 其实就是链表数据结构的实现,所以它具有链表的基本功能,Redis 的 List 的实现为一个 双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。可以用来做最新文章或最新动态。

③Set(集合):

Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺序但都唯一,当你需要存储一个列表数据,又不希望出现重复数据时,Set 是一个很好的选择,你可以基于 Set 轻易实现交集、并集、差集的操作,比如你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。这样的话,Set 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。

④Hash(散列):

Redis 中的 Hash 是一个 String 类型的 field-value(键值对) 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接修改这个对象中的某些字段的值。一般用来存储对象信息。

⑤Zset(有序集合):

和 Set 相比,Sorted Set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表,一般用来做排行榜。

特殊数据结构

①Bitmap

Bitmap 存储的是连续的二进制数字(0 和 1),通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,一般用来表示用户签到情况、活跃用户情况等。

②HyperLogLog

一般用于数量量巨大(百万、千万级别以上)的计数场景

③Geospatial index

主要用于存储地理位置信息,一般用来做附近的人。


String和Hash那种更适合存储对象


  • String 存储的是序列化后的对象数据,存放的是整个对象。Hash 是对对象的每个字段单独存储,可以获取部分字段的信息,也可以修改或者添加部分字段,节省网络流量。如果对象中某些字段需要经常变动或者经常需要单独查询对象中的个别字段信息,Hash 就非常适合。

  • String 存储相对来说更加节省内存,缓存相同数量的对象数据,String 消耗的内存约是 Hash 的一半。并且,存储具有多层嵌套的对象时也方便很多。如果系统对性能和资源消耗非常敏感的话,String 就非常适合。

在绝大部分情况,一般使用 String 来存储对象数据即可!


Redis 是如何判断数据是否过期的呢?


Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。


过期的数据的删除策略


常用的过期数据的删除策略就两个:

  1. 惰性删除 :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。

  1. 定期删除 : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。

定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 定期删除+惰性/懒汉式删除

但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。


Redis 内存淘汰机制


如果过期的数据太多,定时删除无法删除完全(每次删除完过期的 key 还是超过 25%),同时这些 key 也再也不会被客户端请求,也就是无法走惰性删除,这个时候就要用到内存淘汰机制了。

Redis 提供 6 种数据淘汰策略:

  1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

  1. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

  1. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

  1. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)

  1. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

  1. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

4.0 版本后增加以下两种:

  1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰

  1. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key


什么是缓存穿透


缓存穿透说简单点就是大量请求的 key 是不合理的,根本不存在于缓存中,也不存在于数据库中 。这就导致这些请求直接到了数据库上,根本没有经过缓存这一层,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。

解决方案:

一、最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。

二、如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间,具体命令如下: SET key value EX 10086 。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。

三、使用布隆过滤器,布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。我们需要的就是判断 key 是否合法。具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。


什么是缓存击穿


请求的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据已经过期) 。这就可能会导致瞬时大量的请求直接打到了数据库上,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。比如说在一个秒杀活动中,某个商品的数据突然过期,就导致大量请求直接落到数据库中。

解决方案:

一、设置热点数据永不过期或者过期时间比较长。

二、针对热点数据提前预热,将其存入缓存中并设置合理的过期时间比如秒杀场景下的数据在秒杀结束之前不过期。

三、请求数据库写数据到缓存之前,先获取互斥锁,保证只有一个请求会落到数据库上,减少数据库的压力。


什么是缓存雪崩


缓存在同一时间大面积的失效,或者缓存服务器宕机,导致大量的请求都直接落到了数据库上,对数据库造成了巨大的压力。

解决方案:

缓存服务器宕机:

采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。

限流,避免同时处理大量的请求。

热点缓存失效:

随机设置缓存的失效时间。


Redis 集群


模式一:主从模式

redis单节点虽然有通过RDB和AOF持久化机制能将数据持久化到硬盘上,但数据是存储在一台服务器上的,如果服务器出现硬盘故障等问题,会导致数据不可用,而且读写无法分离,读写都在同一台服务器上,请求量大时会出现I/O瓶颈。

为了避免单点故障 和 读写不分离,Redis 提供了复制(replication)功能实现master数据库中的数据更新后,会自动将更新的数据同步到其他slave数据库上。一个master可以有多个salve节点;salve节点可以有slave节点,从节点是级联结构。

主从模式优缺点
  1. 优点: 主从结构具有读写分离,提高效率、数据备份,提供多个副本等优点。

  1. 不足: 最大的不足就是主从模式不具备自动容错和恢复功能,主节点故障,集群则无法进行工作,可用性比较低,从节点升主节点需要人工手动干预。

普通的主从模式,当主数据库崩溃时,需要手动切换从数据库成为主数据库:

  1. 在从数据库中使用SLAVE NO ONE命令将从数据库提升成主数据继续服务。

  1. 启动之前崩溃的主数据库,然后使用SLAVEOF命令将其设置成新的主数据库的从数据库,即可同步数据。

主从复制的流程:

当主从服务器刚建立连接的时候,进行全量同步;全量复制结束后,进行增量复制。当然,如果有需要,slave 在任何时候都可以发起全量同步。

Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份,具体步骤如下:

(1)slave服务器连接到master服务器,便开始进行数据同步,发送psync命令。

(2)master服务器收到psync命令之后,开始执行bgsave命令生成RDB快照文件并使用缓存区记录此后执行的所有写命令如果master收到了多个slave并发连接请求,它只会进行一次持久化,而不是每个连接都执行一次,然后再把这一份持久化的数据发送给多个并发连接的slave。如果RDB复制时间超过60秒(repl-timeout),那么slave服务器就会认为复制失败,可以适当调节大这个参数。

(3)master服务器bgsave执行完之后,就会向所有Slava服务器发送快照文件,并在发送期间继续在缓冲区内记录被执行的写命令client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败。

(4)slave服务器收到RDB快照文件后,会将接收到的数据写入磁盘,然后清空所有旧数据,在从本地磁盘载入收到的快照到内存中,同时基于旧的数据版本对外提供服务。

(5)master服务器发送完RDB快照文件之后,便开始向slave服务器发送缓冲区中的写命令

(6)slave服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

(7)如果slave 节点开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF

Redis复制工作过程

Redis复制的工作过程如上图,主要包含以及几个阶段:

1. 设置IP端口并建立连接

SLAVEOF命令执行后从服务器会根据设置的IP和端口向主服务器创建一个socket连接,socket连接通过后从服务器将发送ping命令到主服务器用于判断当前socket连接的读写是否正常。当主服务器回复为pong是则证明连接成功,如果不为pong则会断开重连。

2. 身份验证并保存主从服务器信息

如果从服务器开启了masterauth选项时则需要进行身份的验证,从服务会向主服务器发送一条带有密码的AUTH命令,主服务器接收到AUTH命令发送来的密码并和自己服务器requirepass选项设置的密码进行匹配,如果一致则验证成功。

主服务器身份验证成功后,从服务器会向主服务器发送当前从服务器监听端口号的执行命令 REPLCONF listening-port XXXX,主服务会将端口号记录在它的从服务器客户端配置中。

3. 数据同步

主从服务器验证完成后,从服务器会向主从服务器发送数据同步的命令,SYNC/PSYNC命令进行数据同步。Redis2.8版本之前时使用SYNC命令,2.8版本之后使用PSYNC命令。下面我们重点会介绍这两种同步的原理。

4. 命令传播和心跳检测

同步操作执行完后。每当主服务器执行写操作的命令后会将改写操作命令发送给从服务器进行执行,从而达到主从服务器数据一致。

在命令传播阶段,从服务器默认会每秒向主服务器发起心跳检测命令:REPLCONF ACK [从服务器的复制偏移量]。主要作用时检测主从服务器的网络连接状态,辅助实现min-slaves选项。

以上则是Redis主从复制的工作过程。接下去重点介绍一下Redis同步的原理。

Redis同步

Redis同步的动作主要时发生在首次建立主从服务连接和主从服务器断开重连。上文中也提到Redis同步有SYNC同步和PSYNC同步。

SYNC同步

在redis2.8版本之前都是SYNC同步。

SYNC同步的主要工作原理:

1.从服务器向主服务器发送sync命令。

2. 主服务器执行bgsave命令,生成rdb文件,并使用一个缓冲区记录从当前开始执行的写命令。

3. 主服务器将rdb文件发送给从服务器,从服务器载入rdb文件。

SYNC同步的缺陷:

每次都需要进行完整的复制,对性能和效率都会有影响。

PSYNC同步

Redis从2.8版本开始使用PSYNC命令替代了SYNC命令,PSYNC同时具有完整同步和部分同步操作。

实现PSYNC同步功能主要有以下三个部分构成:

1. 主/从服务器的复制偏移量

主从服务器在执行复制时都会维护一个复制偏移量,这个复制偏移量在每次复制时会加上这次复制的数据字节数。复制偏移量的作用判断主从服务器的数据是否一致,同时也通过复制偏移量来获取需要同步的数据。

2. 复制积压缓冲区

由主服务器维护的一个固定长度的队列默认大小时1M,主服务器进行命令传播时。将写命令发送给所有从服务器,并将写命令入队到复制积压缓冲区,积压缓冲区会为每个字节记录相应的复制偏移量。当从服务器发起psync命令时会将自己的复制偏移量发送给主服务器,主服务器会从复制缓冲器进行查找,如果存在则执行部分同步,如果不存在则执行完整同步。

3. 服务器运行ID(runID)

每个redis服务器开启时都会生成唯一的一个运行ID,由40个随机的十六进制字符组成,从服务器在连接上主服务器时会将主服务器的runid保存下来。

它的作用主要是当从服务器断线重连主服务器时从服务器会先判断当前连接的主服务器runid与之前记录的主服务器runID是否一致,不一致则执行完整同步,一致则执行部分同步,第二个作用是在哨兵模式下runID也会成为选举主服务器的一个条件(哨兵模式下会提到)。

PSYNC同步的主要工作原理:

1.客户端发起SLAVEOF命令时,首先判断是否为第一次连接复制如果是则执行完整同步,如果不是则进入下一步。

2. 第二步判断当前从服务器连接的主服务runID与之前记录的是否一致,不一致则证明是新的主服务器则进行完整同步。 如果一致则发送部分同步的命令 把从服务器的runID 和复制偏移量发送给主服务器。

3. 主服务器接收到命令后判断该复制偏移量是否在复制积压缓冲区,如果在则执行部分同步,如果不在则执行完整同步,到此数据同步操作完成。

总结

Redis同步分为SYNC和PSYNC同步,SYNC同步时完整数据同步,PSYNC同步实现了部分数据同步。PSYNC同步主要依靠复制偏移量、复制积压缓冲区、服务器runID来实现。

模式二:哨兵模式

第一种主从同步/复制的模式,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用,这时候就需要哨兵模式登场了。

哨兵模式是从Redis的2.6版本开始提供的,但是当时这个版本的模式是不稳定的,直到Redis的2.8版本以后,这个哨兵模式才稳定下来。

哨兵模式核心还是主从复制,只不过在相对于主从模式在主节点宕机导致不可写的情况下,多了一个竞选机制:从所有的从节点竞选出新的主节点。竞选机制的实现,是依赖于在系统中启动一个sentinel进程。

哨兵本身也有单点故障的问题,所以在一个一主多从的Redis系统中,可以使用多个哨兵进行监控,哨兵不仅会监控主数据库和从数据库,哨兵之间也会相互监控。每一个哨兵都是一个独立的进程,作为进程,它会独立运行。

(1)哨兵模式的作用:

监控所有服务器是否正常运行:通过发送命令返回监控服务器的运行状态,处理监控主服务器、从服务器外,哨兵之间也相互监控。

故障切换:当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换master。同时那台有问题的旧主也会变为新主的从,也就是说当旧的主即使恢复时,并不会恢复原来的主身份,而是作为新主的一个从。

(2)哨兵实现原理

哨兵在启动进程时,会读取配置文件的内容,通过如下的配置找出需要监控的主数据库:

sentinel monitor master-name ip port quorum
#master-name是主数据库的名字
#ip和port 是当前主数据库地址和端口号
#quorum表示在执行故障切换操作前,需要多少哨兵节点同意。

这里之所以只需要连接主节点,是因为通过主节点的info命令,获取从节点信息,从而和从节点也建立连接,同时也能通过主节点的info信息知道新增从节点的信息。

一个哨兵节点可以监控多个主节点,但是并不提倡这么做,因为当哨兵节点崩溃时,同时有多个集群切换会发生故障。哨兵启动后,会与主数据库建立两条连接。

  1. 订阅主数据库_sentinel_:hello频道以获取同样监控该数据库的哨兵节点信息

  1. 定期向主数据库发送info命令,获取主数据库本身的信息。

跟主数据库建立连接后会定时执行以下三个操作:

(1)每隔10s向master和 slave发送info命令。作用是获取当前数据库信息,比如发现新增从节点时,会建立连接,并加入到监控列表中,当主从数据库的角色发生变化进行信息更新。

(2)每隔2s向主数据里和从数据库的_sentinel_:hello频道发送自己的信息。作用是将自己的监控数据和哨兵分享。每个哨兵会订阅数据库的_sentinel:hello频道,当其他哨兵收到消息后,会判断该哨兵是不是新的哨兵,如果是则将其加入哨兵列表,并建立连接。

(3)每隔1s向所有主从节点和所有哨兵节点发送ping命令,作用是监控节点是否存活。

(3)主观下线和客观下线

哨兵节点发送ping命令时,当超过一定时间(down-after-millisecond)后,如果节点未回复,则哨兵认为主观下线。主观下线表示当前哨兵认为该节点已经下面,如果该节点为主数据库,哨兵会进一步判断是够需要对其进行故障切换,这时候就要发送命令(SENTINEL is-master-down-by-addr)询问其他哨兵节点是否认为该主节点是主观下线,当达到指定数量(quorum)时,哨兵就会认为是客观下线。

当主节点客观下线时就需要进行主从切换,主从切换的步骤为:

  • 选出领头哨兵。

  • 领头哨兵所有的slave选出优先级最高的从数据库。优先级可以通过slave-priority选项设置。

  • 如果优先级相同,则从复制的命令偏移量越大(即复制同步数据越多,数据越新),越优先。

  • 如果以上条件都一样,则选择run ID较小的从数据库。

选出一个从数据库后,哨兵发送slave no one命令升级为主数据库,并发送slaveof命令将其他从节点的主数据库设置为新的主数据库。

(4)哨兵模式优缺点

1.优点

  • 哨兵模式是基于主从模式的,解决可主从模式中master故障不可以自动切换故障的问题。

2.不足-问题

  • 是一种中心化的集群实现方案:始终只有一个Redis主机来接收和处理写请求,写操作受单机瓶颈影响。

  • 集群里所有节点保存的都是全量数据,浪费内存空间,没有真正实现分布式存储。数据量过大时,主从同步严重影响master的性能。

  • Redis主机宕机后,哨兵模式正在投票选举的情况之外,因为投票选举结束之前,谁也不知道主机和从机是谁,此时Redis也会开启保护机制,禁止写操作,直到选举出了新的Redis主机。

主从模式或哨兵模式每个节点存储的数据都是全量的数据,数据量过大时,就需要对存储的数据进行分片后存储到多个redis实例上。此时就要用到Redis Sharding技术。

模式三:Cluster集群

Redis 的哨兵模式虽然已经可以实现高可用,读写分离 ,但是存在几个方面的不足:

  • 哨兵模式下每台 Redis 服务器都存储相同的数据,很浪费内存空间;数据量太大,主从同步时严重影响了master性能。

  • 哨兵模式是中心化的集群实现方案,每个从机和主机的耦合度很高,master宕机到salve选举master恢复期间服务不可用。

  • 哨兵模式始终只有一个Redis主机来接收和处理写请求,写操作还是受单机瓶颈影响,没有实现真正的分布式架构。

redis在3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的数据。cluster模式为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS不受限于单机,可受益于分布式集群高扩展性。

Redis Cluster是一种服务器Sharding技术(分片和路由都是在服务端实现),采用多主多从,每一个分区都是由一个Redis主机和多个从机组成,片区和片区之间是相互平行的。Redis Cluster集群采用了P2P的模式,完全去中心化。

如上图,官方推荐,集群部署至少要 3 台以上的master节点,最好使用 3 主 3 从六个节点的模式。Redis Cluster集群具有如下几个特点:

  • 集群完全去中心化,采用多主多从;所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。

  • 客户端与 Redis 节点直连,不需要中间代理层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

  • 每一个分区都是由一个Redis主机和多个从机组成,分片和分片之间是相互平行的。

  • 每一个master节点负责维护一部分槽,以及槽所映射的键值数据;集群中每个节点都有全量的槽信息,通过槽每个node都知道具体数据存储到哪个node上。

redis cluster主要是针对海量数据+高并发+高可用的场景,海量数据,如果你的数据量很大,那么建议就用redis cluster,数据量不是很大时,使用sentinel就够了。redis cluster的性能和高可用性均优于哨兵模式。

Redis Cluster采用虚拟哈希槽分区而非一致性hash算法,预先分配一些卡槽,所有的键根据哈希函数映射到这些槽内,每一个分区内的master节点负责维护一部分槽以及槽所映射的键值数据。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值