Redis集群

1      背景

这段时间项目须要对缓存进行集群,下面通过对redis集群研究与測试。对照直接採用单机内存缓存方式的性能。文章记录本人学习与实践redis集群的一些步骤与測试。

2      目标

1. redis集群研究与測试,分析其的可用性、可靠性、可运维性以及性能等方面。

2. 比較单机与集群的性能,内存占用等方面,为重构提供数据分析。

3      Redis集群简单介绍

3.1    前言

为了提供程序的处理能力,一般会把热点数据保存在内存其中而不是直接从数据库中读取。从而减轻数据库压力与提高性能。

Redis就是这样这个优秀的缓存中间件,因为它强大高效而又便捷的功能。得到了广泛的使用。

3.2    Redis集群方案

Redis集群三种常见的解决方式例如以下:

1、client分片:这样的方案将分片工作放在业务程序端,程序代码依据预先设置的路由规则,直接对多个Redis实例进行分布式訪问。

这样的优点是,不依赖于第三方分布式中间件,实现方法和代码都自己掌控,可随时调整。不用操心踩到坑。这实际上是一种静态分片技术。Redis实例的增减。都得手工调整分片程序。

基于此分片机制的开源产品,如今仍不多见。这样的分片机制的性能比代理式更好(少了一个中间分发环节)。但缺点是升级麻烦,对研发人员的个人依赖性强——须要有较强的程序开发能力做后盾。假设主力程序猿离职,可能新的负责人,会选择重写一遍。所以。这样的方式下,可运维性较差。出现问题。定位和解决都得研发和运维配合着解决,故障时间变长。因此这样的方案,难以进行标准化运维。不太适合中小公司(除非有足够的DevOPS)。

2、代理分片:这样的方案。将分片工作交给专门的代理程序来做。代理程序接收到来自业务程序的数据请求,依据路由规则,将这些请求分发给正确的Redis实例并返回给业务程序。这样的机制下。通常会选用第三方代理程序(而不是自己研发),由于后端有多个Redis实例,所以这类程序又称为分布式中间件。这样的优点是。业务程序不用关心后端Redis实例。运维起来也方便。尽管会因此带来些性能损耗,但对于Redis这样的内存读写型应用。相对而言是能容忍的。这是我们推荐的集群实现方案。

像基于该机制的开源产品Twemproxy。Codis便是当中代表,应用很广泛。

3、server端分片:建立在基于无中心分布式架构之上(没有代理节点性能瓶颈问题)。Redis-Cluster即为官方基于该架构的解决方式。RedisCluster将全部Key映射到16384个Slot中,集群中每一个Redis实例负责一部分,业务程序通过集成的RedisClusterclient进行操作。

client能够向任一实例发出请求,假设所需数据不在该实例中。则该实例引导client自己主动去相应实例读写数据。Redis Cluster的成员管理(节点名称、IP、port、状态、角色)等,都通过节点之间两两通讯,定期交换并更新。

 

3.3    Redis集群方案具代表性中间件

中间件名称

特点

Twemproxy

Twemproxy是一种代理分片机制。由Twitter开源。Twemproxy作为代理。可接受来自多个程序的訪问。依照路由规则。转发给后台的各个Redisserver。再原路返回。这个方法顺理成章地攻克了单个Redis实例承载能力的问题。

当然,Twemproxy本身也是单点,须要用Keepalived做高可用方案。这么些年来,Twemproxy是应用范围最广、稳定性最高、最久经考验的分布式中间件。

仅仅是。他还有诸多不方便之处。

Twemproxy最大的痛点在于,无法平滑地扩容/缩容。这样添加了运维难度:业务量突增。需添加Redisserver。业务量萎缩。须要降低Redisserver。但对Twemproxy而言。基本上都非常难操作。或者说。Twemproxy更加像server端静态sharding。有时为了规避业务量突增导致的扩容需求,甚至被迫新开一个基于Twemproxy的Redis集群。Twemproxy还有一个痛点是。运维不友好,甚至没有控制面板。当然,因为使用了中间件代理。相比client直接连server方式,性能上有所损耗,实測结果降低了20%左右。

Codis

Codis由豌豆荚于2014年11月开源,基于Go和C开发,是最近涌现的、国人开发的优秀开源软件之中的一个。现已广泛用于豌豆荚的各种Redis业务场景,从各种压力測试来看。稳定性符合高效运维的要求。性能更是改善非常多,最初比Twemproxy慢20%;如今比Twemproxy快近100%(条件:多实例。一般Value长度)。Codis具有可视化运维管理界面。

Codis无疑是为解决Twemproxy缺点而出的新解决方式。

因此综合方面会因为Twemproxy非常多。眼下也越来越多公司选择Codis。Codis引入了Group的概念,每一个Group包含1个Redis Master及至少1个Redis Slave。这是和Twemproxy的差别之中的一个。

这样做的优点是,假设当前Master有问题,则运维人员可通过Dashboard“自助式”切换到Slave,而不须要小心翼翼地改动程序配置文件。

为支持数据热迁移(Auto Rebalance)。出品方改动了Redis Server源代码。并称之为Codis Server。

Codis採用预先分片(Pre-Sharding)机制。事先规定好了,分成1024个slots(也就是说,最多能支持后端1024个Codis Server),这些路由信息保存在ZooKeeper中。

不足之处有对redis源代码进行了改动。以及代理实现本身会有的问题。

Redis-cluster

reids-cluster在redis3.0中推出,支持Redis分布式集群部署模式。

採用无中心分布式架构。

全部的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.节点的fail是通过集群中超过半数的节点检測失效时才生效.client与redis节点直连,不须要中间proxy层.client不须要连接集群全部节点,连接集群中不论什么一个可用节点就可以,降低了代理层,大大提高了性能。

redis-cluster把全部的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key之间的关系。眼下Jedis已经支持Redis-cluster。

从计算架构或者性能方面无疑Redis-cluster是最佳的选择方案。

(PS:尽管Redis-cluster从方案选型上面比較占领优势,并且官方推出后各方对应挺热。可是因为Redis-cluster刚推出不久。尽管官方宣传已经公布的是文档版本号,但稳定性方面还有待验证)。

 

依据參考官方的更新来看,RedisClister不断趋向于普及与完好,社区的支持程度较热。本文下面主要採用Redis Clister实现集群。并进行測试。

3.4    Redis Clister

Redis3.0 最大的特点就是有了cluster的能力,使用redis-trib.rb工具能够轻松构建Redis Cluster。

Redis Cluster採用无中心结构。每一个节点保存数据和整个集群状态,每一个节点都和其它全部节点连接。

节点之间使用gossip协议传播信息以及发现新节点,这样的结构和Cassandra非常相似,Cassandra节点能够转发请求。Redis集群中节点不作为client请求的代理。client依据node返回的错误信息重定向请求。rediscluster在设计的时候。就考虑到了去中心化,去中间件,也就是说,集群中的每一个节点都是平等的关系。都是对等的,每一个节点都保存各自的数据和整个集群的状态。每一个节点都和其它全部节点连接,并且这些连接保持活跃,这样就保证了我们仅仅须要连接集群中的随意一个节点,就能够获取到其它节点的数据。

3.4.1   集群特性

Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。

Redis集群并不支持处理多个keys的命令。由于这须要在不同的节点间移动数据。从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误。

Redis 集群通过分区来提供一定程度的可用性。在实际环境中当某个节点宕机或者不可达的情况下继续处理命令. Redis 集群的优势:

自己主动切割数据到不同的节点上。

整个集群的部分节点失败或者不可达的情况下可以继续处理命令。

3.4.2   分片

那么redis 是怎样合理分配这些节点和数据的呢?

Redis 集群没有并使用传统的一致性哈希来分配数据,而是採用第二种叫做哈希槽 (hash slot)的方式来分配的。

rediscluster 默认分配了 16384 个slot,当我们set一个key 时,会用CRC16算法来取模得到所属的slot,然后将这个key 分到哈希槽区间的节点上,详细算法就是:CRC16(key) % 16384。

如果如今有3个节点已经组成了集群。各自是:A, B, C 三个节点,它们能够是一台机器上的三个port,也能够是三台不同的server。那么,採用哈希槽 (hash slot)的方式来分配16384个slot 的话,它们三个节点分别承担的slot 区间是:

l  节点A覆盖0-5460;

l  节点B覆盖5461-10922;

l  节点C覆盖10923-16383.

这样的哈希槽的分配方式有好也有坏。优点就是非常清晰。比方我想新增一个节点D,redis cluster的这样的做法是从各个节点的前面各拿取一部分slot到D上。我会在接下来的实践中实验。大致就会变成这样:

l  节点A覆盖1365-5460

l  节点B覆盖6827-10922

l  节点C覆盖12288-16383

l  节点D覆盖0-1364,5461-6826,10923-12287

相同删除一个节点也是类似,移动完毕后就能够删除这个节点了。

所以redis cluster 就是这种一个形状:

 

3.4.3   主从模式

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用。所以集群使用了主从复制模型,每一个节点都会有N-1个复制品。

在我们样例中具有A,B。C三个节点的集群,在没有复制模型的情况下,假设节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。

然而假设在集群创建的时候(或者过一段时间)我们为每一个节点加入一个从节点A1。B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会由于槽找不到而不可用了。

只是当B和B1 都失败后。集群就不可用了。

redis-cluster 选举:


1.      领着选举过程是集群中全部master參与。假设半数以上master节点与master

节点通信超过(cluster-node-timeout)。觉得当前master节点挂掉。

2.      什么时候整个集群不可用(cluster_state:fail)。当集群不可用时,全部对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误

a: 假设集群随意master挂掉,且当前master没有slave。集群进入fail状态,也

能够理解成进群的slot映射[0-16383]不完毕时进入fail状态。

b: 假设进群超过半数以上master挂掉。不管是否有slave集群进入fail状态。

 

3.4.4   一致性

Redis Cluster不提供强一致性。

比如cluster接受了一个写请求,给client返回ok,这个写请求的内容也可能丢失。由于其写流程例如以下:

1. master B接受了一个写请求。

2. B写成功。返回ok给client;

3. B把数据广播给slaves(B1、B2、B3)

假设第二步运行完成后,B crash了,则会发生数据不一致现象。

这与传统的DBMS类似,它们接收了写请求后,每隔1S才会把数据写入disk,这么做也是在性能和一致性之间做一个平衡。(假设用户对数据的一致性要求比較高,Redis日后可能也会兼顾这样的需求。将来会提供对应的选项,让redis中的slave没用成功的接受数据之前不会给client返回ok给client。即先运行step 3。然后再运行step 2。

一致性另一种场景。如果有client Z,与cluster内各个node A and B and C,以及各个node的replica A1 and B1 and C1。Z与B之间连接正常,可是B与B1以及cluster内其它nodes连接失败。如果Z发起write request,那么B会给他返回ok,可是B1无法获取到对应的数据,这就要求写的时候也要把node与cluster内其它的成员的探活也要考虑在内。

基本要求就是,写时间周期要大于探活时间周期(node timeout)。当node Btimeout之后。master B会自己主动进入failing状态,拒绝外部client的连接请求,而cluster则会选出slave B1来取代B。

3.4.5   集群配置參数

cluster-enabled<yes/no>: 是否开启集群

cluster-config-file<filename>:,Redis执行时保存配置的文件,自己主动生成,所以不能改动这个文件。

cluster-node-timeout<milliseconds>: 配置结点超时多久则觉得它失效了。假设是主节点超时被觉得失效,将由其从节点通过故障转移成为主节点。

cluster-slave-validity-factor<factor>: 失效校验次数。连接一个节点超时超过此配置的次数,才觉得失效。比如配置为5。timeout为5秒。则从节点与主节点超时50秒后才会尝试故障转移。

 cluster-migration-barrier <count>: 主节点至少从节点数。

配置该參数,当还有一个主节点没有了从节点覆盖,将对部分从节点进行迁移到该主节点。

cluster-require-full-coverage<yes/no>: 默觉得yes,仅仅要有结点宕机导致16384个槽没全被覆盖,整个集群就所有停止接受写入操作。

假设设置为no,则仍然能够接收部分请求(仍被覆盖的槽相应keys子集)。

3.4.6   Cluster相关命令

集群  :

CLUSTER INFO 打印集群的信息 

CLUSTER NODES 列出集群当前已知的全部节点(node),以及这些节点的相关信息。 

节点  :

CLUSTER MEET <ip> <port> 将 ip 和 port 所指定的节点加入到集群其中,让它成为集群的一份子。 

CLUSTER FORGET <node_id> 从集群中移除 node_id 指定的节点。

 

CLUSTER REPLICATE <node_id> 将当前节点设置为 node_id 指定的节点的从节点。 

CLUSTER SAVECONFIG 将节点的配置文件保存到硬盘里面。 

槽(slot)  :

CLUSTER ADDSLOTS <slot> [slot ...] 将一个或多个槽(slot)指派(assign)给当前节点。 

CLUSTER DELSLOTS <slot> [slot ...] 移除一个或多个槽对当前节点的指派。 

CLUSTER FLUSHSLOTS 移除指派给当前节点的全部槽。让当前节点变成一个没有指派不论什么槽的节点。 

CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点。假设槽已经指派给还有一个节点,那么先让还有一个节点删除该槽>,然后再进行指派。

 

CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽 slot 迁移到 node_id 指定的节点中。

 

CLUSTER SETSLOT <slot> IMPORTING <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点。 

CLUSTER SETSLOT <slot> STABLE 取消对槽 slot 的导入(import)或者迁移(migrate)。 

键  :

CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上。 

CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 眼下包括的键值对数量。

 

CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键。 

这些命令是集群所独有的。运行上述命令要先登录:

redis-cli -c -p6382 -h 172.16.21.139    //登录

 

4      搭建并使用Redis集群

搭建集群的第一件事情我们须要一些执行在集群模式的Redis实例。

这意味这集群并非由一些普通的Redis实例组成的,集群模式须要通过配置启用。开启集群模式后的Redis实例便能够使用集群特有的命令和特性了。

1. 环境

要让集群正常工作至少须要3个主节点,假设不足,创建时会有下面提示:

在这里我们要创建6个redis节点。当中三个为主节点。三个为从节点,相应的redis节点的ip和port相应关系例如以下:

127.0.0.1:7000

127.0.0.1:7001

127.0.0.1:7002

127.0.0.1:7003

127.0.0.1:7004

127.0.0.1:7005

 

2. 下载编译

下载地址: http://download.redis.io/releases/redis-3.0.7.tar.gz  (须要下载3.0.0以上的版本号)

wget http://download.redis.io/releases/redis-3.0.7.tar.gz

tar -zxvf redis-3.0.7.tar.gz

make

 

3. 创建集群须要的文件夹

mkdir /usr/local/redis-3.0.7/cluster

cd /usr/local/redis-3.0.7/cluster

mkdir 7000

mkdir 7001

mkdir 7002

mkdir 7003

mkdir 7004

mkdir 7005

 

4. 配置文件redis.conf

以下是一个最少选项的集群的配置文件。先拷贝一份作为作改动:

cp /usr/local/redis-3.0.7/redis.conf /usr/local/redis-3.0.7/cluster/

vi redis.conf

 

port 7000

daemonize yes

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

appendonly yes

##改动完 redis.conf 配置文件里的这些配置项之后把这个配置文件分别复制到

7000/7001/7002/7003/7004/7005文件夹以下:

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7000

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7001

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7//cluster/7002

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7003

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7004

cp /usr/local/redis-3.0.7/cluster/redis.conf /usr/local/redis-3.0.7/cluster/7005

 

每一个实例配置类似,改动一下都应port、IP地址、文件名就可以。

Redis集群由多个执行在集群模式(cluster mode)下的Redis实例组成,实例的集群模式须要通过配置来开启,开启集群模式的实例将能够使用集群特有的功能和命令。要让集群正常运作至少须要三个主节点, 只是在刚開始试用集群功能时。强烈建议使用六个节点:当中三个为主节点,而其余三个则是各个主节点的从节点。

经常使用配置信息:

绑定地址:bind 192.168.XXX.XXX。不能绑定到127.0.0.1或localhost,否则指导client重定向时会报”Connection refused”的错误。

后台执行:daemonize yes

输出日志:logfile “./redis-7000.log”

监听端口:port 7000

Cluster:cluster-enabled yes

cluster-config-file nodes-7000.conf

cluster-node-timeout 15000

cluster-require-full-coverage yes

 

5. 启动redis实例

cd /usr/local/redis-3.0.7/

./src/redis-server cluster/7000/redis.conf

./src/redis-server cluster/7001/redis.conf

实例打印的日志显示。 由于 nodes.conf 文件不存在,所以每一个节点都为它自身指定了一个新的 ID :

[82462]26 Nov 11:56:55.329 * No cluster configuration found, I'm97a3a64667477371c4479320d683e4c8db5858b1

 

启动之后使用命令查看redis的启动情况 ps –ef | grep redis

 

6. 运行redis的创建集群命令创建集群

使用redis-trib.rb创建集群:

src/redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

# redis-trib.rb 的create子命令构建

# --replicas 则指定了为Redis Cluster中的每一个Master节点配备几个Slave节点

# 节点角色由顺序决定。先master之后是slave

假设运行上述命令报错的时候会报错,由于是运行的ruby的脚本,须要ruby的环境,错误内容:/usr/bin/env: ruby: No such file or directory

所以须要安装ruby的环境,这里推荐使用yuminstall ruby安装

yum install ruby

然后再运行创建集群命令,还会报错。提示不能载入redis,是由于缺少redis和ruby的接口,

错误内容:

/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in`gem_original_require': no such file to load -- redis (LoadError)

from/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'

from./redis-trib.rb:25

使用yum安装:

gem sources -a http://rubygems.org #假设提示https://rubygems.org无法连接,採用http方式

gem install redis

再运行创建集群命令成功后会展示:

输入yes,然后配置完毕。

[OK] All16384 slots covered

这表示集群中的16384个槽都有至少一个主节点在处理。集群运作正常。redis集群即搭建成功!

备注:在utils/create-cluster下提供了一个create-cluster脚本。可以创建出一个集群,类似我们上面建立起的3主3从的集群。

 

7.检查集群状态

src/redis-trib.rb check 127.0.0.1:7000

 

5      測试

5.1    測试说明

   測试场景。分为几种场景測试,分别对单节点单redis实例方式与两台主机多个redis实例分别插入1千万条数据。查询记录等操作。进行性能分析,内存对照。以及对集群的可用性。分别对某个节点进行停止服务,验证其集群的可用性。上述測试都是基于内存方式进行測试。

5.2    本地虚拟主机配置

1、硬件配置:

Ø CPU:Intel(R) Core(TM) i5-4590 CPU @ 3.30GHz * 2(2核处理器)

Ø  内存:4G

Ø  磁盘:75G

2、软件配置:

Ø  操作系统:CentOSLinux release 7.1.1503(Linux version3.10.0-229.14.1.el7.x86_64)

Ø  Redis:3.0.7

Ø  JDK: 1.7.0_79

Ø  Jedis: 2.8.1

 

5.3    測试单机单redis实例

5.3.1   測试场景1

測试场景:使用单机启动redis实例,插入1千万条记录(固定的key值)。

         測试步骤:

1.      改动redis.conf。以全内存方式执行:

快照rdb保存方式部分全凝视了。

#save 900 1

#save 300 10

#save 60 10000

使用aof 持久化方式设置为no

appendfsync no

2.      启动redis实例。

3 . 使用Jedisclient写入1千万条记录(sadd方法),固定的key值,value值模拟规则生成(jedis.sadd("zimpes",s)),得出測试数据。

         备注:这里插入的记录有是有一定的业务逻辑,所以时间上会有所损耗。因此耗时并非直接插入redis的时间。

 

 

 

測试结果

新增记录1千万

測试次数

第一次

第二次

第三次

耗时

1055.999(s)

1102.512(s)

1075.976(s)

内存消耗

955.70(M)

955.70(M)

955.70(M)

 

5.3.2   測试场景2

測试场景:在測试场景1的基础上,运行查询记录1千万次,查询的值随机。

         測试步骤:

1.      同1。以全内存方式启动redis实例。

2 . 使用Jedisclient运行查询记录1千万次(sismember方法)。查询值随机生成,得出測试数据。

 

測试结果

记录1千万,查询次数1千万次

測试次数

第一次

第二次

第三次

耗时

1207.424(s)

1075.278(s)

1024.245(s)

 

5.3.3   測试场景3

測试场景:使用单机启动redis实例。插入1千万条记录(模拟随机生成key值)。

         測试步骤跟測试场景1同样:

1 . 使用Jedisclient写入1千万条记录(sadd方法),模拟随机生成key与value值模(jedis.sadd(s, s))。得出測试数据。

 

 

測试结果

新增记录1千万

測试次数

第一次

第二次

第三次

耗时

1365.425(s)

1366.234(s)

1427.347(s)

内存消耗

1044.34(M)

1044.30(M)

1044.30(M)

 

5.3.4   測试场景4

測试场景:在測试场景3的基础上,运行查询记录1千万次。查询的值随机。

         測试步骤:

1.      同3。以全内存方式启动redis实例。

2 . 使用Jedisclient运行查询记录1千万次(sismember方法),查询值随机生成。得出測试数据。

 

測试结果

记录1千万。查询次数1千万次

測试次数

第一次

第二次

第三次

耗时

 1452.37(s)

1287.173(s)

1408.166(s)

 

5.4    測试多台主机多个redis实例集群

5.4.1   測试场景1

測试场景:使用两台主机。同上述配置。插入1千万条记录。

測试步骤:

1.      分别在两台机器上各启动3个redis实例,分别为:

172.16.21.139:

 

172.16.21.131:

2.      运行../src/redis-trib.rbcreate --replicas 1 172.16.21.139:7000 172.16.21.139:7001 172.16.21.139:7002172.16.21.131:7003 172.16.21.131:7004 172.16.21.131:7005,启动集群:

从启动日志能够看出。三个主节点为:

M: 8092602c5491fad21ab71670a3c178f72964b2ba172.16.21.139:7000

M: e27291b9e2449c857373d4fd60f7b07f37886eed172.16.21.139:7001

M: 22d5c7cce58d559839c2914c34846afc23170320172.16.21.131:7003

三个从节点为:

S: 576e6ea7e133510dfa5e90d086e1330846f6fdd7172.16.21.139:7002

S: e46fc51206f05160dff40a7a585908bd55e9e9b5172.16.21.131:7004

S: 356a53b3146e0f8918d3530eba3d463f94397350172.16.21.131:7005

 

3 . 使用Jedisclient写入1千万条记录(sadd方法),測试发现假设採用同样的key值时,仅仅存储到同一个节点,这样測试并没有多大意义,因此这里測试採用的key与value使用的都是一样,都是模拟随机生成的,从而得出測试数据。

 

主机一

 


主机二

 

 


測试结果

新增记录1千万

測试次数

第一次

第二次

第三次

耗时

1014.444(s)

952.875(s)

962.408(s)

内存消耗

(主机1)

661.45+661.52+660.9

=1983.87(M)

661.51+661.86+660.41

=1983.78(M)

660.8+660.68+660.29

=1981.77(M)

内存消耗

(主机2)

661.9+660.47+660.5

=1982.87(M)

661.4+660.49+660.82

=1982.71(M)

661.31+661.77+661.72

=1984.8(M)

内存消耗

(总)

3966.74(M)

3966.49(M)

3966.57(M)

 

 

5.4.2   測试场景2

測试场景:在測试场景1的基础上。运行查询记录1千万次,查询的值随机。

         測试步骤:

1.      同1,以全内存方式启动redis实例。

2 . 使用Jedisclient运行查询记录1千万次(sismember方法),查询值随机生成,得出測试数据。

 

測试结果

 

主机一:

 

主机二:

 

记录1千万。查询次数1千万次

測试次数

第一次

第二次

第三次

耗时

1910.489(s)

1971.054(s)

1942.552(s)

 

 

 

5.4.3   可用性、可运维性測试

測试场景:使用两台主机,同上述配置。起分别6个redis实例并对其进行集群配置。分别測试加入新节点,Resharding,故障转移,移除节点等实验。

实验主要通过redis-trib.rb命令进行操作。能够查询到相关命令信息:

[root@localhost src]# ./redis-trib.rb help

5.4.3.1  加入节点

1.      參考之前章节4搭建并使用Redis集群。在已经启动6个节点集群的基础上,在139机器上建立7010目录和对应改动port为7010的redis.conf文件;在131机器上建立7020目录和对应改动port为7020的redis.conf文件。并分别启动两个Redis实例:

[root@localhost cluster]# ../src/redis-server ./7010/redis.conf

[root@localhost cluster]# ../src/redis-server ./7020/redis.conf

 

当前的集群信息:

 

2.      使用程序模拟随机生成记录,保持到redis。数据分布例如以下:

3.      使用redis-trib.rb add-node分别将两个新结点加入到集群中,一个作为Master,一个作为其Slave。

加入Master节点

[root@localhost src]# ./redis-trib.rb add-node 172.16.21.139:7010 172.16.21.139:7000

 

新加入的7010节点已经作为master节点,没有包括不论什么数据, 由于它没有包括不论什么 slot。

,当集群须要将某个从节点升级为新的主节点时,这个新节点不会被选中。

加入Slaver节点

./redis-trib.rb add-node --slave --master-id 6dc6b0be6f1eb4b0316c25856ea2a08361f2cf05 172.16.21.131:7020 172.16.21.139:7000

 

注意:在线加入slaver时,须要dump整个master进程,并传递到slaver,再由slaver

载入 rdb 文件到内存,rdb 传输过程中Master可能无法提供服务。整个过程消耗大量 io。小心操作。

 

 

5.4.3.2  Resharding

新加入的节点仍不能提供服务,须要为其分配slot后才行。通过redis-trib.rbreshard能够交互式地迁移Slot。以下的样例将4000个Slot从其它master节点迁移到7010节点上。也能够通过./redis-trib.rb reshard <host>:<port> --from<node-id> --to <node-id> --slots --yes在程序中自己主动完毕迁移。

1.      reshard:

./redis-trib.rb reshard 172.16.21.139:7000

途中会进行询问:

须要迁移的slot数量

选择要接受这些迁移slot的node-id

选择slot来源,all表示从全部的master又一次分配,或者数据要提取 slot 的 master 节点 id,最后用 done 结束

打印被移动的 slot 后,输入 yes 開始移动 slot 以及相应的数据

此时7010已成功分配4000个slot:

 

2.      迁移完毕后。查看之前保存的測试记录的分布情况,能够看到部分Key已经迁移到了新的结点7010上。

 

5.4.3.3  故障转移

停掉主节点

Redis Cluster重用了Sentinel的代码逻辑。不须要单独启动一个Sentinel集群,RedisCluster本身就能自己主动进行Master选举和Failover切换。

1.      先查看7010眼下存储的数据记录:

 

2.      停掉7010节点。之后能够看到结点状态变成了fail。而Slave 7011被选举为新的Master。

 

3.      再次查询之前保存在7010节点上的数据。能够看到7020节点顶替了7010节点成为主节点后继续提供服务。整个集群没有受到影响。

 

4.      又一次启动7010,能够发现自己主动又一次恢复到集群中。

 

5.      然后尝试用程序不断写入记录,中途停止7020节点。

 

此时自己主动切换7010为master节点:

程序仍能够进行写入,表示集群故障转移是成功的。

 

重新启动7020节点的实例。连接上去查看如今数据量与7010节点上的数据量。发现是一致的。表示数据主从同步成功。

 

注意:这里使用的是Jedis,初始化JedisCluster时有几个參数须要重点关注的:

connectionTimeout

soTimeout        socket timeout

maxRedirections 尝试重定向次数

以上connectionTimeout与soTimeout须要大于redis配置failover检測主节点,终于从节点成功替代原主节点的时间段。

不然会抛出CLUSTER DOWN异常。

另外maxRedirections尝试重定向次数将包括在上面两个配置的时间段内,假设插入条数速度比較快此值须要设置比較大,不然在从节点生效之前,已经到达最大重定向次数的话。将会抛出Too many Cluster redirections?异常。

 

5.4.3.4  删除节点

1.      删除节点前。查询当前集群信息:

 

2.      分别进行删除从节点与删除主节点測试

删除从节点

./redis-trib.rb del-node 172.16.21.131:7020 fe79dcbac250b97ec9df4102983fd2901f4823c6

运行完毕后查看集群状态与7020节点实例,发现都已经没有了:

 

删除主节点

使用相同的方法移除主节点,只是在移除主节点前。须要确保这个主节点是空的。假设不是空的,须要将这个节点的数据又一次分片到其它主节点上。

将7010节点的slot(4000)迁到7000上:

./redis-trib.rb reshard 172.16.21.139:7000

输入要迁移的slot数量。这里是4000

输入要接受这些slot的node-id,这里是7000节点的node-id

输入被分配的源节点node-id,这里须要被删除7010节点的node-id

输入done之后能够看到后台输出moving的信息:

完毕后能够查询到7010节点的slots迁移到7000中了:

 

最后就能够运行删除主节点操作

./redis-trib.rb del-node 172.16.21.139:7010 6dc6b0be6f1eb4b0316c25856ea2a08361f2cf05

 

运行完毕后查看集群状态与7020节点实例。发现都已经没有了:

 

5.4.3.5  迁移从节点

1.      在特定的场景下,不须要系统管理员的协助下,自己主动将一个从节点从当前的主节点切换到还有一个主节的自己主动又一次配置的过程叫做复制迁移(从节点迁移),从节点的迁移可以提升整个Redis集群的可用性:

概况一下从节点迁移

集群会在有从节点数量最多的主节点上进行从节点的迁移

要在一个主节点上加入多个从节点

參数来控制从节点迁移replica-migration-barrier:你能够细致阅读redis.conf。

 

2.      先查询当前集群的信息:


 

3.      启动7020节点实例。并加入到集群中作为7000的从节点:

./redis-trib.rb add-node --slave --master-id d3e9e5fac92b43aee69f3f29d4679842ee7e979b 172.16.21.131:7020 172.16.21.139:7000

4.      加入成功后能够看到,7020已作为7000的从节点。此时7000的从节点有7004和7020两个:


5.      7003仅仅有一个从节点7002,尝试删除7002从节点:

./redis-trib.rb del-node 172.16.21.139:7002 559dee1a662ecdc508efcf402c4f19294e3b42dd

 

6.      删除后已经没有了7002实例,与集群中7003已没有从节点:

 

7.      使用replicate转移7020从节点到7003主节点,成功后例如以下:

 

5.4.3.6  手动故障转移

有的时候在主节点没有不论什么问题的情况下强制手动故障转移也是非常有必要的,比方想要升级主节点的Redis进程,我们能够通过故障转移将其转为slave再进行升级操作来避免对集群的可用性造成非常大的影响。

Redis集群使用 CLUSTER FAILOVER命令来进行故障转移,只是要被转移的主节点的从节点上运行该命令 手动故障转移比主节点失败自己主动故障转移更加安全,由于手动故障转移时client的切换是在确保新的主节点全然复制了失败的旧的主节点数据的前提下下发生的,所以避免了数据的丢失。

运行手动故障转移时从节点日志例如以下:

# Manual failover user request accepted.

# Received replication offset for pausedmaster manual failover: 347540

# All master replication stream processed,manual failover can start.

# Start of election delayed for 0milliseconds (rank #0, offset 347540).

# Starting a failover election for epoch7545.

# Failover election won: I'm the newmaster.

其基本步骤例如以下:client不再链接我们淘汰的主节点,同一时候主节点向从节点发送复制偏移量,从节点得到复制偏移量后故障转移開始,接着通知主节点进行配置切换,当client在旧的master上解锁后又一次连接到新的主节点上。

 

比如当前7002是7003的从节点,通过登录7002运行failover。主动实现故障迁移:

 

运行后,能够看到7002已经成为主节点,7003成为其从节点:

 

5.5    结果分析

1.      单机上保存1千万条记录(当前生成规则:key固定值与value模拟生成规则值)占用内存大小约为955M。比較稳定。因此能够预计到1亿条记录大约至少须要9.4G内存。

2.      单机上保存1千万条记录(当前生成规则:key与value都是模拟生成规则值)占用内存大小约为1044.30M,比較稳定,因此能够预计到1亿条记录大约至少须要10.2G内存。

插入数据的性能比第一种情况下降15%-28%。查询的性能比第一种情况下降15%-25%。

3.      集群的情况下保存1千万条记录(当前生成规则:key与value都是模拟生成规则值),平均每台机占用总内存大小约为1984M,共约3966.6M,因此能够预计到1亿条记录大约至少共须要38.7G内存。

4.      相同的加入记录条件下(当前生成规则:key与value都是模拟生成规则值)。新增记录1千万条,单机约为1400s,集群约为977s。集群性能提升大约30%。

5.      相同的查询记录条件下(当前生成规则:key与value都是模拟生成规则值)。存量记录1千万条。查询1千万次。单机约为1382s,集群约为1941s。集群性能损耗大约29%。

6.      Redis cluster採用Master-Slaver模式。自己主动检測Master节点是否停掉,进行切换,整个集群的部分节点失败或者不可达的情况下可以继续处理命令,满足高可用性。

7.      Redis cluster提供加入节点,删除节点。迁移从节点等特性。通过提供的命令非常方便地对集群进行水平伸缩。



本文章了採用下面这些巨人的文章,很感谢他们的分享:

http://www.redis.cn/topics/cluster-tutorial.html

http://blog.csdn.net/dc_726/article/details/48552531

http://ihenu.iteye.com/blog/2267881

http://blog.csdn.net/zhu_tianwei/article/details/44928779



posted on 2017-07-24 14:05  blfbuaa  阅读(212)  评论(0编辑  收藏  举报