各位老铁们好,相信很多人对redis缓存服务器都不是特别的了解,因此呢,今天就来为大家分享下关于redis缓存服务器以及三种模式搭建和运行原理的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!

一、面试官:Redis中的缓冲区了解吗-

hello大家好,我是七淅(xī)。

Redis大家肯定不陌生,但在使用层面看不到的地方,就容易被忽略。今天想和大家分享的内容是 Redis各个缓冲区的作用、溢出的后果及优化方向。

在开始正文前,想多叨叨几句。不管是 Redis还是其他中间件,底层很多原理都是相似的,设计思想都是通用的。

大家以后如果在学什么新框架/组件,可以尽量和已经学过的知识点进行联想,这样会更容易理解点的,不至于说死记硬背。

比如现在说到的缓冲区,它的目的是什么呢?

无它,为了*能。

要么缓存数据,提高响应速度。比如 MySQL中有个 change buffer

要么担心消费者速度跟不上生产,怕数据丢失。所以需要把生产数据先暂存起来。Redis的缓冲区就是这个作用。

另外,消费者速度跟不上,如果是同步处理的话,那是不是也会拖慢生产者,所以这里其实也是在保证生产者的速度。

可能有的读者会说:扯淡,消费者都跟不上了,生产者再快有什么用?

其实有没有一种可能,生产者根本不关心消费者什么时候用呢?前者是负责把后者需要的东西处理好给它就完事了。生产者很忙,还有其他一大堆数据要处理,不能慢慢等消费者同步消费完才去做其他事情。

好像开头扩展得有点多,我收一收,下面会详细说到。有疑问的小伙伴请上车,七淅正式发车了。

首先 Redis有什么缓冲区呢?

一共 4个:

服务器端会给每个连接的客户端都设置了一个输入缓冲区。

暂存请求数据。

输入缓冲区会先把客户端发送过来的命令暂存起来,Redis主线程再从输入缓冲区中读取命令,进行处理。

为了避免客户端和服务器端的请求发送和处理速度不匹配,这点和等下要说的输出缓冲区是一样的。

首先缓冲区是一块固定大小的内存区域,如果要把这个地方填满的话,那 Redis会把客户端连接关闭。

保护自己嘛,你客户端挂了总比我服务端挂了好,服务端一挂就是所有客户端都没用了。

那填满缓冲区就有 2个情况了:

那么把上述原理对应到 Redis的场景。

一下子填满的情况可以是往 Redis里写大量数据,百万千万数量级那种。

另一个情况可以是 Redis服务端因执行耗时操作,阻塞住了,导致没法消费输入缓冲区数据。

对应上面 2个溢出场景,优化方向很自然就有了。

一下子填满的情况,是不是可以考虑不要一下子写这么多数据,能否拆下数据(其实一下子写大量数据本身就不合理哈)

另外,是否可以调高缓冲区大小呢?

redis缓存服务器 三种模式搭建和运行原理

这个其实是不行的哈,因为没有可以设置的地方,目前服务端默认为每个客户端输入缓冲区分配的大小是 1GB。

那轮到第 2个溢出场景:两边处理速度不一致。

正常来说,服务端不应该出现长时间阻塞,所以需要看看是什么原因导致的阻塞,解决到就好了。

同输入缓冲区,服务器端也会给每个连接的客户端都设置了一个输出缓冲区。

同上,也是暂存请求数据。

这个地方其实我在文章开头说的,生产者不关心消费者什么时候用,只负责把消费者之前请求的东西处理好就完事了。

服务端一般都会和多个客户端连接,加上 redis网络通信模块是单线程的(即使是新版本支持多线程也一样)

假如没有输出缓冲区会发生什么事呢?

服务端处理了很多客户端 A的请求,需要经过网络这一耗时操作,返回给客户端 A。在这个过程中,客户端 B的请求一直得不到服务端处理和响应,这样吞吐量就上不去了。

有了缓冲区之后,至少能解放服务端,让它去处理客户端 B的请求。

这里也是同输入缓冲区,我就不啰嗦了,溢出的话服务端也会关闭客户端连接。

类似的,不要一下子读大量数据;不持续在线上执行 MONITOR命令。

而输出缓冲区的大小是可以通过 client-output-buffer-limit来设置的。

但是一般来说,我们都不用改,因为默认情况就够了,这里了解下就好。

温馨提示下,如果对 Redis同步/复制不了解的读者,比如不知道全量/增量复制,建议可以看下我这篇文章:一文让你明白Redis主从同步。

下面回到正题哈。

有复制肯定有主从,而主从间的数据复制包括全量复制和增量复制两种。

全量复制是同步所有数据,而增量复制只会把主从库网络断连期间主库收到的命令,同步给从库。

暂存数据。

主节点上会为每个从节点都维护一个复制缓冲区。

在全量复制时,主节点在向从节点传输 RDB文件的同时,会继续接收客户端发送的写命令请求,并保存在复制缓冲区中,等 RDB文件传输完成后,再发送给从节点去执行。

从节点接收和加载 RDB较慢,同时主节点接收到了大量的写命令,写命令在复制缓冲区中就会越积越多,最后就会溢出。

一旦溢出,主节点会关闭和从节点进行复制操作的连接,导致全量复制失败

可以控制主节点数据量在 2~4GB(仅供参考),这样可以让全量同步执行得更快些,避免复制缓冲区累积过多命令

也可以调整缓冲区大小,还是之前的 client-output-buffer-limit参数。

比如: config set client-output-buffer-limit sl*e 512mb 128mb 60

这个是在新增复制用到的缓冲区。

暂存数据。

从节点意外断开连接后重连,可从该缓冲区同步期间没同步到的数据。

不会溢出。(想不到吧.jpg)

该缓冲区本质是一个固定长度,先进先出的队列,默认 1MB。

所以当队列被占满,不是报错,也不像上面几个缓冲区关闭连接。而是覆盖最早进入队列的数据。

因此,如果有从节点还没有同步这些旧命令数据,就会导致主从节点重新进行全量复制,而不是增量复制。

调整复制积压缓冲区的大小,参数是: repl_backlog_size

二、缓存***redis***三种模式搭建和运行原理

标签: redis缓存主从哨兵集群

本文简单的介绍redis三种模式在linux的安装部署和数据存储的总结,希望可以相互交流相互提升。

对于Centos7在安装redis之前需要进行一些常用工具的安装:

关闭防火墙

正式安装redis

在redis进行maketest时候会出现一系列的异常,有如下解决方案:

用redis-server启动一下redis,做一些实验没什么意义。

要把redis作为一个系统的daemon进程去运行的,每次系统启动,redis进程一起启动,操作不走如下:

RDB和AOF是redis的一种数据持久化的机制。持久化是为了避免系统在发生灾难*的系统故障时导致的系统数据丢失。我们一般会将数据存放在本地磁盘,还会定期的将数据上传到云服务器。

RDB是redis的snapshotting,通过redis.conf中的s*e配置进行设置,如 s*e 60 1000:

AOF是以endonly方式进行数据的储存的,开启AOF模式后,所有存进redis内存的数据都会进入os cache中,然后默认1秒执行一次fsync写入追加到endonly.aof文件中。一般我们配置redis.conf中的一下指令:

AOF和RDB模式我们一般在生产环境都会打开,一般而言,redis服务挂掉后进行重启会优先家在aof中的文件。

当启动一个sl*e node的时候,它会发送一个PSYNC命令给master node,如果这是sl*e node重新连接master node,那么master node仅仅会复制给sl*e部分缺少的数据;否则如果是sl*e node第一次连接master node,那么会触发一次full resynchronization;

开始full resynchronization的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB文件生成完毕之后,master会将这个RDB发送给sl*e,sl*e会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给sl*e,sl*e也会同步这些数据。

sl*e node如果跟master node有网络故障,断开了连接,会自动重连。master如果发现有多个sl*e node都来重新连接,仅仅会启动一个rdb s*e操作,用一份数据服务所有sl*e node。

从redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。

master node会在内存中常见一个backlog,master和sl*e都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和sl*e网络连接断掉了,sl*e会让master从上次的replica offset开始继续复制,但是如果没有找到对应的offset,那么就会执行一次resynchronization。

master在内存中创建rdb,然后发送给sl*e,不会在自己本地落地磁盘了,可以有如下配置:

sl*e不会过期key,只会等待master过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送给sl*e。

在redis.conf配置文件中,上面的参数代表至少需要3个sl*es节点与master节点进行连接,并且master和每个sl*e的数据同步延迟不能超过10秒。一旦上面的设定没有匹配上,则master不在提供相应的服务。

sdown达成的条件很简单,如果一个哨兵ping一个master,超过了 is-master-down-after-milliseconds指定的毫秒数之后,就主观认为master宕机

sdown到odown转换的条件很简单,如果一个哨兵在指定时间内,收到了 quorum指定数量的其他哨兵也认为那个master是sdown了,那么就认为是odown了,客观认为master宕机

如果一个sl*e跟master断开连接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么sl*e就被认为不适合选举为master

(down-after-milliseconds* 10)+ milliseconds_since_master_is_in_SDOWN_state

每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown,然后选举出一个sl*e来做切换,这个sl*e还得得到majority哨兵的授权,才能正式执行切换;

(2)SENTINEL RESET*,在所有sentinal上执行,清理所有的master状态

(3)SENTINEL MASTER mastername,在所有sentinal上执行,查看所有sentinal对数量是否达成了一致

4.3.2 sl*e的永久下线

让master摘除某个已经下线的sl*e:SENTINEL RESET mastername,在所有的哨兵上面执行.

redis的集群模式为了解决系统的横向扩展以及海量数据的存储问题,如果你的数据量很大,那么就可以用redis cluster。

redis cluster可以支撑N个redis master,一个master上面可以挂载多个sl*e,一般情况我门挂载一个到两个sl*e,master在挂掉以后会主动切换到sl*e上面,或者当一个master上面的sl*e都挂掉后,集群会从其他master上面找到冗余的sl*e挂载到这个master上面,达到了系统的高可用*。

2.1 redis cluster的重要配置

2.2在三台机器上启动6个redis实例

将上面的配置文件,在/etc/redis下放6个,分别为: 7001.conf,7002.conf,7003.conf,7004.conf,7005.conf,7006.conf

每个启动脚本内,都修改对应的端口号

2.3创建集群

解决办法是先安装rvm,再把ruby版本提升至2.3.3

使用redis-trib.rb命令创建集群

--replicas:表示每个master有几个sl*e

redis-trib.rb check 192.168.31.187:7001查看状体

3.1加入新master

以上相同配置完成后,设置启动脚本进行启动;然后用如下命令进行node节点添加:

3.2 reshard一些数据过去

3.3添加node作为sl*e

3.4删除node

三、Redis分布式缓存搭建

花了两天时间整理了之前记录的Redis单体与哨兵模式的搭建与使用,又补齐了集群模式的使用和搭建经验,并对集群的一些个原理做了理解。

笔者安装中遇到的一些问题:

如果make报错,可能是没装g或者g++编辑器,安装之 yum-y install g g-c++ kernel-devel,有可能还是提示一些个c文件编译不过,g-v查看下版本,如果不到5.3那么升级一下g:

在/etc/profile追加一行 source/opt/rh/devtoolset-9/enable

scl enable devtoolset-9 bash

重新make clean, make

这回编译通过了,提示让你最好make test一下/

执行make test,如果提示 You need tcl 8.5 or newer in order to run the Redis test

那就升级tcl, yum install tcl

重新make test,如果还有error就删了目录,重新tar包解压重新make, make test

\o/ All tests passed without errors!,表示编译成功。

然后make install即可。

运行命令:./redis-server/usr/redis-6.0.3/redis.conf&

redis.conf配置文件里 bind 0.0.0.0设置外部访问, requirepass xxxx设置密码。

redis高可用方案有两种:

常用搭建方案为1主1从或1主2从+3哨兵监控主节点,以及3主3从6节点集群。

(1)sentinel哨兵

/usr/redis-6.0.3/src/redis-sentinel/usr/redis-6.0.3/sentinel2.conf&

sentinel2.conf配置:

坑1:master节点也会在故障转移后成为从节点,也需要配置masterauth

当kill master进程之后,经过sentinel选举,sl*e成为了新的master,再次启动原master,提示如下错误:

原因是此时的master再次启动已经是sl*e了,需要向现在的新master输入密码,所以需要在master.conf

中配置:

坑2:哨兵配置文件要暴露客户端可以访问到的master

在 sentinel.conf配置文件的 sentinel monitor mymaster 122.xx.xxx.xxx 6379 2中,配置该哨兵对应的master名字、master和端口,以及达到多少个哨兵选举通过认为master挂掉。其中master要站在redis访问者(也就是客户端)的角度、配置访问者能访问的,例如sentinel与master在一台服务器(122.xx.xxx.xxx)上,那么相对sentinel其master在本机也就是127.0.0.1上,这样 sentinel monitor mymaster 127.0.0.1 6379 2逻辑上没有问题,但是如果另外服务器上的springboot通过lettuce访问这个redis哨兵,则得到的master为127.0.0.1,也就是springboot所在服务器本机,这显然就有问题了。

附springboot2.1 redis哨兵配置:

坑3:要注意配置文件.conf会被哨兵修改

redis-cli-h localhost-p 26379,可以登到sentinel上用info命令查看一下哨兵的信息。

曾经遇到过这样一个问题,大致的信息如下

sl*es莫名其妙多了一个,master的也明明改了真实对外的,这里又变成127.0.0.1!

最后,把5个redis进程都停掉,逐个检查配置文件,发现redis的配置文件在主从哨兵模式会被修改,master的配置文件最后边莫名其妙多了一行replicaof 127.0.0.1 7001,怀疑应该是之前配置错误的时候(见坑2)被哨兵动态加上去的!总之,实践中一定要多注意配置文件的变化。

(2)集群

当数据量大到一定程度,比如几十上百G,哨兵模式不够用了需要做水平拆分,早些年是使用codis,twemproxy这些第三方中间件来做分片的,即客户端->中间件-> Redis server这样的模式,中间件使用一致*Hash算法来确定key在哪个分片上。后来Redis官方提供了方案,大家就都采用官方的Redis Cluster方案了。

Redis Cluster从逻辑上分16384个hash slot,分片算法是 CRC16(key) mod 16384得到key应该对应哪个slot,据此判断这个slot属于哪个节点。

每个节点可以设置1或多个从节点,常用的是3主节点3从节点的方案。

reshard,重新分片,可以指定从哪几个节点移动一些hash槽到另一个节点去。重新分片的过程对客户端透明,不影响线上业务。

搭建Redis cluster

redis.conf文件关键的几个配置:

启动6个集群节点

[root@VM_0_11_centos redis-6.0.3]# ps-ef|grep redis

root 5508 1 0 21:25? 00:00:00/usr/redis-6.0.3/src/redis-server 0.0.0.0:7001 [cluster]

root 6903 1 0 21:32? 00:00:00/usr/redis-6.0.3/src/redis-server 0.0.0.0:7002 [cluster]

root 6939 1 0 21:33? 00:00:00/usr/redis-6.0.3/src/redis-server 0.0.0.0:7003 [cluster]

root 6966 1 0 21:33? 00:00:00/usr/redis-6.0.3/src/redis-server 0.0.0.0:7004 [cluster]

root 6993 1 0 21:33? 00:00:00/usr/redis-6.0.3/src/redis-server 0.0.0.0:7005 [cluster]

root 7015 1 0 21:33? 00:00:00/usr/redis-6.0.3/src/redis-server 0.0.0.0:7006 [cluster]

这时候这6个节点还是独立的,要把他们配置成集群:

说明:-a xxxx是因为笔者在redis.conf中配置了requirepass xxxx密码,然后--cluster-replicas 1中的1表示每个master节点有1个从节点。

上述命令执行完以后会有一个询问: Can I set the above configuration? yes同意自动做好的分片即可。

最后 All 16384 slots covered.表示集群中16384个slot中的每一个都有至少有1个master节点在处理,集群启动成功。

查看集群状态:

坑1:暴露给客户端的节点不对

使用lettuce连接发现连不上,查看日志 Connection refused: no further information:/127.0.0.1:7002,跟之前哨兵配置文件sentinel.conf里边配置master犯的错误一样,集群启动的时候带的应该是提供给客户端访问的。

我们要重建集群:先把6个redis进程停掉,然后删除 nodes-7001.conf这些节点配置文件,删除持久化文件 dump.rdb、 endonly.aof,重新启动6个进程,在重新建立集群:

然后,还是连不上,这次报错 connection timed out:/172.xx.0.xx:7004,发现连到企鹅云服务器的内网上了!

解决办法,修改每个节点的redis.conf配置文件,找到如下说明:

所以增加配置:

然后再重新构建集群,停进程、改配置、删除节点文件和持久化文件、启动进程、配置集群。。。再来一套(累死了)

重新使用Lettuce测试,这次终于连上了!

坑2:Lettuce客户端在master节点故障时没有自动切换到从节点

name这个key在7002上,kill这个进程模拟master下线,然后Lettuce一直重连。我们期望的是应该能自动切换到其sl*e 7006上去,如下图:

重新启动7002进程,

7006已成为新master,7002成为它的sl*e,然后Lettuce也能连接上了。

解决办法,修改Lettuce的配置:

笔者用的是springboot 2.1 spring-boot-starter-data-redis默认的Lettuce客户端,当使用Redis cluster集群模式时,需要配置一下 RedisConnectionFactory开启自适应刷新来做故障转移时的自动切换从节点进行连接。

重新测试:停掉master 7006,这次Lettuce可以正常切换连到7002sl*e上去了。(仍然会不断的在日志里报连接错误,因为需要一直尝试重连7006,但因为有7002从节点顶上了、所以应用是可以正常使用的)

Redis不保证数据的强一致*

Redis并不保证数据的强一致*,也就是取CAP定理中的AP

关于一致*Hash算法,可以参考一致*Hash算法-(jianshu.)

Redis cluster使用的是hash slot算法,跟一致*Hash算法不太一样,固定16384个hash槽,然后计算key落在哪个slot里边(计算key的CRC16值再对16384取模),key找的是slot而不是节点,而slot与节点的对应关系可以通过reshard改变并通过gossip协议扩散到集群中的每一个节点、进而可以为客户端获知,这样key的节点寻址就跟具体的节点个数没关系了。也同样解决了普通hash取模算法当节点个数发生变化时,大量key对应的寻址都发生改动导致缓存失效的问题。

比如集群增加了1个节点,这时候如果不做任何操作,那么新增加的这个节点上是没有slot的,所有slot都在原来的节点上且对应关系不变、所以没有因为节点个数变动而缓存失效,当reshard一部分slot到新节点后,客户端获取到新迁移的这部分slot与新节点的对应关系、寻址到新节点,而没迁移的slot仍然寻址到原来的节点。

关于热迁移,猜想,内部应该是先做复制迁移,等迁移完了,再切换slot与节点的对应关系,复制没有完成之前仍按照原来的slot与节点对应关系去原节点访问。复制结束之后,再删除原节点上已经迁移的slot所对应的key。

与哨兵模式比较类似,当1个节点发现某个master节点故障了、会对这个故障节点进行pfail主观宕机,然后会通过gossip协议通知到集群中的其他节点、其他节点也执行判断pfail并gossip扩散广播这一过程,当超过半数节点pfail时那么故障节点就是fail客观宕机。接下来所有的master节点会在故障节点的从节点中选出一个新的主节点,此时所有的master节点中超过半数的都投票选举了故障节点的某个从节点,那么这个从节点当选新的master节点。

所有节点都持有元数据,节点之间通过gossip这种二进制协议进行通信、发送自己的元数据信息给其他节点、故障检测、集群配置更新、故障转移授权等等。

这种去中心化的分布式节点之间内部协调,包括故障识别、故障转移、选主等等,核心在于gossip扩散协议,能够支撑这样的广播协议在于所有的节点都持有一份完整的集群元数据,即所有的节点都知悉当前集群全局的情况。

Redis高可用方案-(jianshu.)

面试题:Redis集群模式的工作原理能说一下么-云+社区-腾讯云(tencent.)

深度图解Redis Cluster原理- detectiveHLH-*客园(blogs.)

Redis学习笔记之集群重启和遇到的坑-阿里云开发者社区(aliyun.)

云服务器Redis集群部署及客户端通过公网IP连接问题