Redis

一:基本数据类型  

  1.String

  #一个key对应一个value
  set name 小明 get name

  2.Hash hash

    特别适合用于存储对象

set user:1 username xiaoming password 123 sex nan
get user:1 hgetall
get user:1 usernmae

  3.List

    Redis 列表是简单的字符串列表,按照插入顺序排序,最先插入排在最后,最后插入排在最前

lpush redis.net.cn redis lpush redis.net.cn mongodb lpush redis.net.cn mq
    lrange redis.net.cn 0 10
    1) "mq"
    2) "mongodb"
    3) "redis"

  4.Set

    Redis的Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

    sadd 命令 添加一个string元素到,key对应的set集合中,成功返回1,如果元素以及在集合中返回0,key对应的set不存在返回错误。
 

sadd redis.net.cn redis sadd redis.net.cn mongodb sadd redis.net.cn rabitmq sadd redis.net.cn rabitmq
    SMEMBERS redis.net.cn
    1) "rabitmq"
    2) "mongodb"
    3) "redis"

  5.zset(sorted set:有序集合)

    zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序

zadd mykey 0 mongodb zadd mykey 0 redis zadd mykey 0 mysql
    zrange mykey 0 1
    1) "mongodb"
    2) "mysql"
    zrange mykey 0 2
    1) "mongodb"
    2) "mysql"
    3) "redis"

   6.Redis 在 2.8.9 版本添加了 HyperLogLog 结构,Redis HyperLogLog 是用来做基数统计的算法
    比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

二:事务

  Redis事务的例子, 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务
  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
redis 127.0.0.1:6379> MULTI
  OK
 
  redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
  QUEUED
 
  redis 127.0.0.1:6379> GET book-name
  QUEUED
 
  redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
  QUEUED
 
  redis 127.0.0.1:6379> SMEMBERS tag
  QUEUED
 
  redis 127.0.0.1:6379> EXEC
  1) OK
  2) "Mastering C++ in 21 days"
  3) (integer) 3
  4) 1) "Mastering Series"
  2) "C++"
  3) "Programming"

三:数据备份

数据备份
  Redis SAVE 命令用于创建当前数据库的备份。该命令将在 redis 安装目录中创建dump.rdb文件。
  创建 redis 备份文件也可以使用命令 BGSAVE,该命令在后台执行
恢复数据
  如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis 安装目录并启动服务即可。获取 redis 目录可以使用 CONFIG 命令,如下所示:

redis 127.0.0.1:6379> CONFIG GET dir
  1) "dir"
  2) "/usr/local/redis/bin"

以上命令 CONFIG GET dir 输出的 redis 安装目录为 /usr/local/redis/bin。

Redis 管道技术
  Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应

四:Redis高可用

在Redis中,实现高可用的技术主要包括持久化、复制、哨兵和集群,下面分别说明它们的作用,以及解决了什么样的问题。

持久化:持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
复制:复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。
  缺陷:故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
哨兵:在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。
集群:通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。


1.持久化:

Redis持久化分为RDB持久化和AOF持久化:RDB将当前数据保存到硬盘,AOF则是将每次执行的写命令保存到硬盘(类似于MySQL的binlog)
由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地

RDB持久化是将当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),保存的文件后缀是rdb;当Redis重新启动时,可以读取快照文件恢复数据

2触发条件:RDB持久化的触发分为手动触发和自动触发两种
1)手动触发:save命令和bgsave命令都可以生成RDB文件。
save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求。
而bgsave命令会创建一个子进程,由子进程来负责创建RDB文件,父进程(即Redis主进程)则继续处理请求。
bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程都会阻塞服务器
此外,在自动触发RDB持久化时,Redis也会选择bgsave而不是save来进行持久化
2)自动触发:save m n
2.1 自动触发最常见的情况是在配置文件中通过save m n,指定当m秒内发生n次变化时,会触发bgsave
save 900 1 save 300 10 save 60 10000 当三个save条件满足任意一个时,都会引起bgsave的调用

3)save m n的实现原理
Redis的save m n,是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。
serverCron是Redis服务器的周期性操作函数,默认每隔100ms执行一次;该函数对服务器的状态进行维护,其中一项工作就是检查 save m n 配置的条件是否满足,如果满足就执行bgsave。
dirty计数器是Redis服务器维持的一个状态,记录了上一次执行bgsave/save命令后,服务器状态进行了多少次修改(包括增删改);而当save/bgsave执行完成后,会将dirty重新置为0。
例如,如果Redis执行了set mykey helloworld,则dirty值会+1;如果执行了sadd myset v1 v2 v3,则dirty值会+3;注意dirty记录的是服务器进行了多少次修改,而不是客户端执行了多少修改数据的命令。
lastsave时间戳也是Redis服务器维持的一个状态,记录的是上一次成功执行save/bgsave的时间。
save m n的原理如下:每隔100ms,执行serverCron函数;在serverCron函数中,遍历save m n配置的保存条件,只要有一个条件满足,就进行bgsave。对于每一个save m n条件,只有下面两条同时满足时才算满足:
(1)当前时间-lastsave > m
(2)dirty >= n
3.其他自动触发机制:
除了save m n 以外,还有一些其他情况会触发bgsave:
在主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点
执行shutdown命令时,自动执行rdb持久化

4.执行流程 bgsave命令的执行流程
1) Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(后面会详细介绍该命令)的子进程,如果在执行则bgsave命令直接返回。bgsave/bgrewriteaof 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。
2) 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令
3) 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令
4) 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换
5) 子进程发送信号给父进程表示完成,父进程更新统计信息

 RDB文件是经过压缩的二进制文件

5.AOF持久化
AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中(有点像MySQL的binlog);当Redis重启时再次执行AOF文件中的命令来恢复数据。
1. 开启AOF Redis服务器默认开启RDB,关闭AOF;要开启AOF,需要在配置文件中配置:
appendonly yes
2. 执行流程
由于需要记录Redis的每条写命令,因此AOF不需要触发,下面介绍AOF的执行流程。
AOF的执行流程包括:
命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;
文件重写(rewrite):定期重写AOF文件,达到压缩的目的

系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。

AOF缓存区的同步文件策略由参数appendfsync控制,各个值的含义如下:
always:命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF文件,硬盘IO成为性能瓶颈,Redis只能支持大约几百TPS写入,严重降低了Redis的性能;即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低SSD的寿命。
no:命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证。
everysec:命令写入aof_buf后调用系统write操作,write完成后线程返回;fsync同步文件操作由专门的线程每秒调用一次。everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置。

3. 文件重写(rewrite)
随着时间流逝,Redis服务器执行的写命令越来越多,AOF文件也会越来越大;过大的AOF文件不仅会影响服务器的正常运行,也会导致数据恢复需要的时间过长。

文件重写是指定期重写AOF文件,减小AOF文件的体积。需要注意的是,AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作!

文件重写之所以能够压缩AOF文件,原因在于:
过期的数据不再写入文件
无效的命令不再写入文件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset)等等
多条命令可以合并为一个:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为sadd myset v1 v2 v3。不过为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset类型的key,并不一定只使用一条命令;而是以某个常量为界将命令拆分为多条。这个常量在redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD中定义,不可更改,3.0版本中值是64

4.文件重写的触发
文件重写的触发,分为手动触发和自动触发:
手动触发:直接调用bgrewriteaof命令,该命令的执行与bgsave有些类似:都是fork子进程进行具体的工作,且都只有在fork时阻塞
自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数,以及aof_current_size和aof_base_size状态确定触发时机。
auto-aof-rewrite-min-size:执行AOF重写时,文件的最小体积,默认值为64MB。
auto-aof-rewrite-percentage:执行AOF重写时,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值。
只有当auto-aof-rewrite-min-size和auto-aof-rewrite-percentage两个参数同时满足时,才会自动触发AOF重写,即bgrewriteaof操作

关于文件重写的流程,有两点需要特别注意:
(1)重写由父进程fork子进程进行;
(2)重写期间Redis执行的写命令,需要追加到新的AOF文件中,为此Redis引入了aof_rewrite_buf缓存

文件重写的流程如下:
1) Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在bgsave命令则等bgsave执行完成后再执行。前面曾介绍过,这个主要是基于性能方面的考虑。
2) 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的。
3) 父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程,并可以响应其他命令。Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确。
4) 由于fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然在响应命令,因此Redis使用AOF重写缓冲区(图中的aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区。
5) 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。
6) 子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体可以通过info persistence查看。
7) 父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致。
8) 使用新的AOF文件替换老文件,完成AOF重写

 启动时加载
前面提到过,当AOF开启时,Redis启动时会优先载入AOF文件来恢复数据;只有当AOF关闭时,才会载入RDB文件恢复数据。

 AOF常用配置总结
下面是AOF常用的配置项,以及默认值;前面介绍过的这里不再详细介绍。

appendonly no:是否开启AOF
appendfilename "appendonly.aof":AOF文件名
dir ./:RDB文件和AOF文件所在目录
appendfsync everysec:fsync持久化策略
no-appendfsync-on-rewrite no:AOF重写期间是否禁止fsync;如果开启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢失AOF重写期间的数据;需要在负载和安全性之间进行平衡
auto-aof-rewrite-percentage 100:文件重写触发条件之一
auto-aof-rewrite-min-size 64mb:文件重写触发提交之一
aof-load-truncated yes:如果AOF文件结尾损坏,Redis启动时是否仍载入AOF文件


5.RDB和AOF的优缺点
RDB持久化
优点:RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小。
缺点:RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。

AOF持久化
与RDB持久化相对应,AOF的优点在于支持秒级持久化、兼容性好,缺点是文件大、恢复速度慢、对性能影响大。

在Redis的实践中,众多因素限制了Redis单机的内存不能过大,例如:
当面对请求的暴增,需要从库扩容时,Redis内存过大会导致扩容时间太长;
当主机宕机时,切换主机后需要挂载从库,Redis内存过大导致挂载速度过慢;
以及持久化过程中的fork操作

首先说明一下fork操作:
父进程通过fork操作可以创建子进程;子进程创建后,父子进程共享代码段,不共享进程的数据空间,但是子进程会获得父进程的数据空间的副本。在操作系统fork的实际实现中,基本都采用了写时复制技术,即在父/子进程试图修改数据空间之前,父子进程实际上共享数据空间;但是当父/子进程的任何一个试图修改数据空间时,操作系统会为修改的那一部分(内存的一页)制作一个副本。
虽然fork时,子进程不会复制父进程的数据空间,但是会复制内存页表(页表相当于内存的索引、目录);父进程的数据空间越大,内存页表越大,fork时复制耗时也会越多


6.主从复制概述
  主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

主从复制的作用
主从复制的作用主要包括:
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。

主从复制 127.0.0.1:6380> slaveof 127.0.0.1 6379 指定服务为主服务

主从复制的实现原理
主从复制过程大体可以分为3个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段
1. 连接建立阶段
该阶段的主要作用是在主从节点之间建立连接,为数据同步做好准备。
步骤1:保存主节点信息
从节点服务器内部维护了两个字段,即masterhost和masterport字段,用于存储主节点的ip和port信息。
需要注意的是,slaveof是异步命令,从节点完成主节点ip和port的保存后,向发送slaveof命令的客户端直接返回OK,实际的复制操作在这之后才开始进行。
步骤2:建立socket连接
从节点每秒1次调用复制定时函数replicationCron(),如果发现了有主节点可以连接,便会根据主节点的ip和port,创建socket连接。如果连接成功,则:
从节点:为该socket建立一个专门处理复制工作的文件事件处理器,负责后续的复制工作,如接收RDB文件、接收命令传播等。
主节点:接收到从节点的socket连接后(即accept之后),为该socket创建相应的客户端状态,并将从节点看做是连接到主节点的一个客户端,后面的步骤会以从节点向主节点发送命令请求的形式来进行
步骤3:发送ping命令
从节点成为主节点的客户端之后,发送ping命令进行首次请求,目的是:检查socket连接是否可用,以及主节点当前是否能够处理请求。
从节点发送ping命令后,可能出现3种情况:
(1)返回pong:说明socket连接正常,且主节点当前可以处理请求,复制过程继续。
(2)超时:一定时间后从节点仍未收到主节点的回复,说明socket连接不可用,则从节点断开socket连接,并重连。
(3)返回pong以外的结果:如果主节点返回其他结果,如正在处理超时运行的脚本,说明主节点当前无法处理命令,则从节点断开socket连接,并重连
步骤4:身份验证
步骤5:发送从节点端口信息
身份验证之后,从节点会向主节点发送其监听的端口号(前述例子中为6380),主节点将该信息保存到该从节点对应的客户端的slave_listening_port字段中;该端口信息除了在主节点中执行info Replication时显示以外,没有其他作用

2. 数据同步阶段
具体执行的方式是:从节点向主节点发送psync命令(Redis2.8以前是sync命令),开始同步,根据主从节点当前状态的不同,可以分为全量复制和部分复制
在数据同步阶段之前,从节点是主节点的客户端,主节点不是从节点的客户端;而到了这一阶段及以后,主从节点互为客户端。原因在于:在此之前,主节点只需要响应从节点的请求即可,不需要主动发请求,而在数据同步阶段和后面的命令传播阶段,主节点需要主动向从节点发送请求(如推送缓冲区中的写命令),才能完成复制。
3. 命令传播阶段


7.哨兵模式概述:

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题 。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理就是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例

这里的哨兵有两个作用:
通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
当哨兵检测到master宕机,会自动将slave切换成master,然后通过发布订阅模式 通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover(故障转移)过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。
当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。
切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。

8.redis的Cluster概述

# 介绍
1.redis cluster,主要是针对海量数据+高并发+高可用的场景。redis cluster 支撑 N 个 redis master node,每个 master node 都可以挂载多个 slave node。
这样整个 redis 就可以横向扩容了。如果你要支撑更大数据量的缓存,那就横向扩容更多的 master 节点,每个 master 节点就能存放更多的数据了。
换句话说就是,自动将数据进行分片,每个 master 上放一部分数据。提供内置的高可用支持,部分 master 不可用时,还是可以继续工作的。
2.在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端口号,比如 16379。16379 端口号是用来进行节点间通信的,也就是 cluster bus 的东西,
cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus用了另外一种二进制的协议,gossip协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。

 分布式寻址算法
1. hash 算法(大量缓存重建)
  来了一个 key,首先计算 hash 值,然后对节点数取模。然后打在不同的 master 节点上。一旦某一个 master 节点宕机,所有请求过来,都会基于最新的剩余 master 节点数去取模,尝试去取数据。
这会导致大部分的请求过来,全部无法拿到有效的缓存,导致大量的流量涌入数据库。
2.一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡)
  一致性 hash 算法将整个 hash 值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash。这样就能确定每个节点在其哈希环上的位置。
来了一个 key,首先计算 hash 值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,遇到的第一个 master 节点就是 key 所在位置。
在一致性哈希算法中,如果一个节点挂了,受影响的数据仅仅是此节点到环空间前一个节点(沿着逆时针方向行走遇到的第一个节点)之间的数据,其它不受影响。增加一个节点也同理。
 燃鹅,一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。
这样就实现了数据的均匀分布,负载均衡。
3.redis cluster 的 hash slot 算法
 redis cluster 有固定的16384个 hash slot,对每个key计算CRC16值,然后对16384取模,可以获取 key 对应的 hash slot。
 redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot。
 hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。
 移动 hash slot 的成本是非常低的。客户端的 api,可以对指定的数据,让他们走同一个 hash slot,通过hash tag来实现。
 任何一台机器宕机,另外两个节点,不影响的。因为 key 找的是 hash slot,不是机器。

 Redis cluster的高可用和主备切换原理
 redis cluster的高可用原理基本跟哨兵是一样的
 1.判断节点宕机
    主观宕机:如果一个节点认为另外一个节点宕机,那么就是pfail
    客观宕机:如果多个节点都认为另外一个节点宕机了,那么就是fail
 在cluster-node-timeout内,某个节点一直没有返回pong,那么就被认为pfail。
 如果一个节点认为某个节点pfail了,那么会在gossip ping消息中,ping给其他节点,如果超过半数的节点都认为pfail了,那么就会变成fail。
 2.从节点过滤
    对宕机的 master node,从其所有的 slave node 中,选择一个切换成 master node。
 检查每个 slave node 与 master node 断开连接的时间,如果超过了cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成master。
 3.从节点选举
   每个从节点,都根据自己对 master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。
 所有的 master node 开始 slave 选举投票,给要进行选举的 slave 进行投票,如果大部分 master node(N/2 + 1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成 master。从节点执行主备切换,从节点切换为主节点。
 4.与哨兵比较
  整个流程跟哨兵相比,非常类似,所以说,redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能。

 gossip协议
 gossip 协议包含多种消息,包含ping,pong,meet,fail等等。
 1.meet:某个节点发送 meet 给新加入的节点,让新节点加入集群中,然后新节点就会开始与其它节点进行通信。redis-trib.rb add-node。其实内部就是发送了一个 gossip meet消息给新加入的节点,通知那个节点去加入我们的集群。
 2.ping:每个节点都会频繁给其它节点发送 ping,其中包含自己的状态还有自己维护的集群元数据,互相通过 ping 交换元数据。
 3.pong:返回 ping 和 meeet,包含自己的状态和其它信息,也用于信息广播和更新。
 4.fail:某个节点判断另一个节点 fail 之后,就发送 fail 给其它节点,通知其它节点说,某个节点宕机啦。
 ping消息深入
 ping 时要携带一些元数据,如果很频繁,可能会加重网络负担。
 每个节点每秒会执行 10 次 ping,每次会选择 5 个最久没有通信的其它节点。当然如果发现某个节点通信延时达到了cluster_node_timeout / 2,那么立即发送 ping,避免数据交换延时过长,落后的时间太长了。
 比如说,两个节点之间都 10 分钟没有交换数据了,那么整个集群处于严重的元数据不一致的情况,就会有问题。所以cluster_node_timeout可以调节,如果调得比较大,那么会降低 ping 的频率。
 每次 ping,会带上自己节点的信息,还有就是带上 1/10 其它节点的信息,发送出去,进行交换。至少包含3个其它节点的信息,最多包含总节点数减2个其它节点的信息。

 redis cluster节点间的内部通讯机制
 集群元数据的维护有两种方式:集中式、Gossip协议。redis cluster节点间采用 gossip协议进行通信。
 1.集中式
 (1)是将集群元数据(节点信息、故障等等)几种存储在某个节点上。集中式元数据集中存储的一个典型代表,就是大数据领域的storm。它是分布式的大数据实时计算引擎,是集中式的元数据存储的结构,
 底层基于 zookeeper(分布式协调的中间件)对所有元数据进行存储维护。
 (2)好处在于,元数据的读取和更新,时效性非常好,一旦元数据出现了变更,就立即更新到集中式的存储中,其它节点读取的时候就可以感知到
 (3)不好在于,所有的元数据的更新压力全部集中在一个地方,可能会导致元数据的存储有压力。
 2.gossip协议
 (1)所有节点都持有一份元数据,不同的节点如果出现了元数据的变更,就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更。
 (2)好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续打到所有节点上去更新,降低了压力
 (3)不好在于,元数据的更新有延时,可能导致集群中的一些操作会有一些滞后。
 1> 10000 端口:每个节点都有一个专门用于节点间通信的端口,就是自己提供服务的端口号+10000,比如 7001,那么用于节点间通信的就是 17001端口。
 每个节点每隔一段时间都会往另外几个节点发送ping消息,同时其它几个节点接收到ping之后返回pong。
 2> 交换的信息:信息包括故障信息,节点的增加和删除,hash slot 信息等等。

 redis集群模式的工作原理
 1.自动将数据进行分片,每个master上放一部分数据.提供内置的高可用支持,部分master不可用时,还是可以继续工作的。
 支撑N个redis master node,每个master node都可以挂载多个slave node高可用,因为每个master都有salve节点,那么如果mater挂掉,redis cluster这套机制,就会自动将某个slave切换成master
 2.如果你的数据量很少,主要是承载高并发高性能的场景,比如你的缓存一般就几个G,单机足够了
 replication,一个mater,多个slave,要几个slave跟你的要求的读吞吐量有关系,然后自己搭建一个sentinal集群,去保证redis主从架构的高可用性,就可以了
 3.如果你的数据量很大,redis cluster,主要是针对海量数据+高并发+高可用的场景,海量数据,如果你的数据量很大,那么建议就用redis cluster

 

 五:过期策略

定期删除
Redis每隔100ms就随机抽取一些设置了过期时间的key,判断其是否过期,如果过期就删除了。
惰性删除 # Redis的key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。
定时删除 # 在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除。
定期删除、惰性删除仍然会存在大量过期key堆积在内存中的情况。定时删除虽然内存会及时释放,但是十分消耗CPU资源。大并发下,CPU应该用在处理请求上而不是删除key上。
内存淘汰策略
1.volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
2.volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
3.volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
4.allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
5.allkeys-random:从数据集中任意选择数据淘汰 # 6.no-enviction(驱逐):禁止驱逐数据,新写入操作会报错
两个误区
1.数据莫名丢失 # Redis是基于内存的,而内存是有限的,假如内存容量全部存满,再继续往里存的时候,就会顶掉一部分数据,就会造成数据莫名其妙就丢失了
2.设置了过期时间不代表就会被删除,数据过期不代表就意味着会被从内存中删除掉,只是标记了一下。所以可能会造成时间到了,内存占用没有下降的原因。

 

 六:缓存雪崩、缓存穿透、缓存击穿技术方案

 缓存雪崩 

 1.当缓存服务器重启或大量缓存在一个时间段都同时失效,那么请求来的时候在缓存中查不到,会再去数据库中查,请求过多就可能会导致系统崩溃。这就是缓存雪崩。
 2.举个例子:对于系统 A,假设每天高峰期每秒5000个请求,本来缓存在高峰期可以扛住每秒4000个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时1秒5000个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。
 此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。这就是缓存雪崩。
 3.缓存雪崩的事前事中事后的解决方案如下。
 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
 用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 redis。如果 ehcache 和 redis 都没有,再查数据库,将数据库中的结果,写入ehcache和redis 中。
 限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?走降级!可以返回一些默认的值,或者友情提示,或者空白的值。
 好处:
 (1)数据库绝对不会死,限流组件确保了每秒只有多少个请求能通过。
 (2)只要数据库不死,就是说,对用户来说,2/5 的请求都是可以被处理的。
 (3)只要有 2/5 的请求可以被处理,就意味着你的系统没死,对用户来说,可能就是点击几次刷不出来页面,但是多点几次,就可以刷出来一次。

 缓存穿透
 1.正常的缓存系统都是先根据key去缓存中查找,查到了就返回,查不到就再去数据库中查,那么一些恶意的请求会故意查询大量的缓存中没有的key,
 这就会使得这些请求都去访问数据库,请求量越大,后端系统的压力就越大,最后就很有可能导致崩溃,这就叫做缓存穿透。
 2.举个例子:对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。黑客发出的那 4000 个攻击,缓存中查不到,每次你去数据库里查,也查不到。比如查的id都是负数
 这样的话,缓存中不会有,请求每次都直接查询数据库。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。
 3.解决方式很简单,每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如set -999 UNKNOWN。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。

 缓存击穿 1.跟缓存雪崩类似,区别在于这里针对某一个key缓存,而雪崩是针对很多key。
 2.举个例子:缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
 3.解决方式也很简单,可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。

 

posted @   慢品人间烟火色  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· 面试官:你是如何进行SQL调优的?
点击右上角即可分享
微信分享提示