Redis 的集群策略

当redis 内存过大时, 必须使用集群。 而主从和sentinel 的方式都是一台机器作为master,数据量过大时候, 还是撑不住。通常采取Cluster模式。下面文字参考了其他的一些文章。仅供自己总结。

redis集群的三种模式

image.png

通过持久化功能,Redis保证了即使在服务器重启的情况下也不会丢失(或少量丢失)数据,因为持久化会把内存中数据保存到硬盘上,重启会从硬盘上加载数据。但是由于数据是存储在一台服务器上的,如果这台服务器出现硬盘故障等问题,也会导致数据都是。

为了避免单点故障,通常的做法是将数据库复制多个副本以部署在不同的服务器上,这样即使有一台服务器出现故障,其他服务器已让可以继续提供服务。为此,**Redis提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。**

在复制的概念中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。主数据库可以进行读写操作,当写操作导致数据变化会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。

主从数据库的配置:

主数据库不用配置,从数据库的配置文件(redis.conf)中可以加载主数据库的信息,也可以在启动时,使用 redis-server --port 6380 --slaveof 127.0.0.1 6379 命令指明主数据库的 IP 和端口。从数据库一般是只读,可以改为可写,但写入的数据很容易被主同步没,所以还是只读就可以。也可以在运行时使用 slaveof ip port 命令,停止原来的主,切换成刚刚设置的主 slaveof no one会把自己变成主。

主从复制原理:

image
  • 从数据库连接主数据库,发送SYNC命令;
  • 主数据库接收到SYNC命令后,可以执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
  • 主数据库BGSAVE执行完后,向所有从数据库发送快照文件,并在发送期间继续记录被执行的写命令;
  • 从数据库收到快照文件后丢弃所有旧数据,载入收到的快照;
  • 主数据库快照发送完毕后开始向从数据库发送缓冲区中的写命令;
  • 从数据库完成对快照的载入,开始接受命令请求,并执行来自主数据库缓冲区的写命令;(从数据库初始化完成)
  • 主数据库每执行一个写命令就会向从数据库发送相同的写命令,从数据库接收并执行收到的写命令(从数据库初始化完成后的操作)
  • 出现断开重连后,2.8之后的版本会将断线期间的命令传给从数据库,增量复制。
  • 主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave在任何时候都可以发起全量同步。Redis的策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

优点:

  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离;
  • 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务依然必须由Master来完成;
  • Slave同样可以接受其他Slaves的连接和同步请求,这样可以有效地分载Master的同步压力;
  • Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求;
  • Slave Server同样是以阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则范湖同步之前的数据。

缺点:

  • Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复;
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性;
  • 如果多个Slave断线了,需要重启的时候,尽量不要在同一时间段进行重启。因为只要Slave启动,就会发送sync请求和主机全量同步,当多个Slave重启的时候,可能会导致Master IO剧增从而宕机。
  • Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂;

二、哨兵模式

第一种主从同步/复制的模式,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。

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


image.png

哨兵模式的作用:

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


    image.png

故障切换的过程:

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

每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。
如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN)
如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态
当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)
在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
优点:

哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
主从可以自动切换,系统更健壮,可用性更高。
缺点:

Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。

三、Cluster 集群

Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的内容。

image.png

集群的配置

根据官方推荐,集群部署至少要 3 台以上的master节点,最好使用 3 主 3 从六个节点的模式。在测试环境中,只能在一台机器上面开启6个服务实例来模拟。

1、修改配置文件

将 redis.conf 的配置文件复制6份(文件名最好加上端口后缀),然后开始修改配置文件中的参数

# 开启redis的集群模式
cluster-enabled yes 
# 配置集群模式下的配置文件
cluster-config-file nodes-6379.conf
 # 集群内几点之间支持最长响应时间
cluster-node-timeout 15000

2、修改完毕之后启动 6 个 Redis 服务

3、快速部署集群

6个 Redis 服务启动成功之后,借助 redis-tri.rb 工具可以快速的部署集群,如果本机没有该命令行需要自行安装,只需要执行/redis-trib.rb create --replicas 1 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 就可以成功创建集群。

集群的特点

  • 所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  • 节点的fail是通过集群中超过半数的节点检测失效时才生效。
  • 客户端与Redis节点直连,不需要中间代理层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

集群的工作方式

在 Redis 的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的 Key到达的时候,Redis 会根据 crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。

我比较好奇的是在进群工作条件下,如果增加节点,这个需要数据重新调整。 所有的数据需要rehash,来进行分配。如果数据非常大, 这个是非常耗时的操作。如果进行这方面的操作。redis 也有很好的解决方案。
一、集群伸缩原理

    Redis集群提供了灵活的节点扩容和收缩方案。在不影响集群对外服务的情况下,可以为集群添加节点进行扩容也可以下线部分节点进行缩容:
image.png

我们都指导,这样的每一个节点上面都分配了我们的16384槽中的几个,以及对应槽下面的数据。所以我们在伸缩节点的时候,实质上也是对于哈希槽和槽对应数据的一个调整。

首先我们先来看一下经典集群搭建的分布图:


image.png

三个主节点分别维护自己负责的槽和对应的数据,如果希望加入1个节点实现集群扩容时,需要通过相关命令把一部分槽和数据迁移给新节点:

image.png

总结:集群伸缩就是槽和数据在节点之间移动。

二、集群扩容

我们的集群在扩容的时候,是进行的增加节点的操作,故需要把已有节点上的槽和数据分配到新的节点上,那么应该把已有节点上的哪些槽和节点放在新节点上呢?让我们来探究。

扩容是分布式存储最常见的需求,Redis集群扩容操作可分为如下步骤:

准备新节点。
加入集群。
迁移槽和数据。
1.准备新节点

首先我们需要运行命令来启动两个新的redis节点:

redis-server conf/redis-6385.conf
redis-server conf/redis-6386.conf
启动之后,这两个redis节点为孤立的节点

2、加入集群

127.0.0.1:6379> cluster meet 127.0.0.1 6385
127.0.0.1:6379> cluster meet 127.0.0.1 6386
执行上述命令加入已有的集群。

image.png
image.png
    集群内新旧节点经过一段时间的ping/pong消息通信之后,所有节点会发 现新节点并将它们的状态保存到本地。这个时间按照我们集群的约定时间来确定。

    新节点刚开始都是主节点状态,但是由于没有负责的槽,所以不能接受任何读写操作。对于新节点的后续操作我们一般有两种选择:

为它迁移槽和数据实现扩容。
作为其他主节点的从节点负责故障转移。
redis-trib.rb工具也实现了为现有集群添加新节点的命令,还实现了直接添加为从节点的支持,命令如下:

redis-trib.rb add-node new_host:new_port existing_host:existing_port --slave --master-id <arg>
使用命令直接添加从节点:

redis-trib.rb add-node 127.0.0.1:6385 127.0.0.1:6379
redis-trib.rb add-node 127.0.0.1:6386 127.0.0.1:6379
3、迁移槽和数据

     槽是Redis集群管理数据的基本单位,首先需要为新节点制定槽的迁移计划,确定原有节点的哪些槽需要迁移到新节点。迁移计划需要确保每个节点负责相似数量的槽,从而保证各节点的数据均匀。例如,在集群中加入 6385节点,如图所示。加入6385节点后,原有节点负责的槽数量从 6380变为4096个。
image.png

槽迁移计划确定后开始逐个把槽内数据从源节点迁移到目标节点。

迁移数据:

数据迁移过程是逐个槽进行的,流程如下:

1)对目标节点发送cluster setslot{slot}importing{sourceNodeId}命令,让目标节点准备导入槽的数据。
2)对源节点发送cluster setslot{slot}migrating{targetNodeId}命令,让源节点准备迁出槽的数据。
3)源节点循环执行cluster getkeysinslot{slot}{count}命令,获取count个 属于槽{slot}的键。

image.png
image.png

4)在源节点上执行migrate{targetIp}{targetPort}""0{timeout}keys{keys...} 命令,把获取的键通过流水线(pipeline)机制批量迁移到目标节点,批量 迁移版本的migrate命令在Redis3.0.6以上版本提供,之前的migrate命令只能 单个键迁移。对于大量key的场景,批量键迁移将极大降低节点之间网络IO次数。
5)重复执行步骤3)和步骤4)直到槽下所有的键值数据迁移到目标节点。
6)向集群内所有主节点发送cluster setslot{slot}node{targetNodeId}命令,通知槽分配给目标节点。为了保证槽节点映射变更及时传播,需要遍历发送给所有主节点更新被迁移的槽指向新节点。

三、集群收缩

    收缩集群意味着缩减规模,需要从现有集群中安全下线部分节点。
image.png
image.png

流程说明:
1)首先需要确定下线节点是否有负责的槽,如果是,需要把槽迁移到其他节点,保证节点下线后整个集群槽节点映射的完整性。
2)当下线节点不再负责槽或者本身是从节点时,就可以通知集群内其他节点忘记下线节点,当所有的节点忘记该节点后可以正常关闭。

1、下线迁移槽

  下线节点需要把自己负责的槽迁移到其他节点,原理与之前节点扩容的 迁移槽过程一致。例如我们把6381和6384节点下线,节点信息如下:

127.0.0.1:6381> cluster nodes 
40b8d09d44294d2e23c7c768efc8fcd153446746 127.0.0.1:6381 myself,master - 0 0 2 connected     
12288-16383 4fa7eac4080f0b667ffeab9b87841da49b84a6e4 127.0.0.1:6384 slave 
40b8d09d44294d2e2    3c7c768efc8fcd153446746 0 1469894180780 5 connected ...

6381是主节点,负责槽(12288-16383),6384是它的从节点,如图所示。下线6381之前需要把负责的槽迁移到其他节点。


image.png

收缩正好和扩容迁移方向相反,6381变为源节点,其他主节点变为目标 节点,源节点需要把自身负责的4096个槽均匀地迁移到其他主节点上。

我们可以使用用redis-trib.rb reshard命令完成槽的迁移:

#redis-trib.rb reshard 127.0.0.1:6381 
>>> Performing Cluster Check (using node 127.0.0.1:6381)
 ... 
[OK] All 16384 slots covered. 
How many slots do you want to move (from 1 to 16384)1365 
What is the receiving node ID cfb28ef1deee4e0fa78da86abe5d24566744411e /*输入6379    
节点id作为目标节点.*/ 
Please enter all the source node IDs. 
Type 'all' to use all the nodes as source nodes for the hash slots. 
Type 'done' once you entered all the source nodes IDs. 
Source node #1:40b8d09d44294d2e23c7c768efc8fcd153446746 /*源节点6381 id*/ 
Source node #2:done /* 输入done确认 */ 
... 
Do you want to proceed with the proposed reshard plan (yes/no) yes 
...

这样完成了1365个槽往6379节点上面迁移,同样的工作做三遍,即可迁移所有节点到其他节点上。

2、忘记节点

由于集群内的节点不停地通过Gossip消息彼此交换节点状态,因此需要
通过一种健壮的机制让集群内所有节点忘记下线的节点。也就是说让其他节 点不再与要下线节点进行Gossip消息交换。Redis提供了cluster forget{downNodeId}命令实现该功能:

   当节点接收到cluster forget{down NodeId}命令后,会把nodeId指定的节 点加入到禁用列表中,在禁用列表内的节点不再发送Gossip消息。禁用列表 有效期是60秒,超过60秒节点会再次参与消息交换。也就是说当第一次 forget命令发出后,我们有60秒的时间让集群内的所有节点忘记下线节点。

   线上操作不建议直接使用cluster forget命令下线节点,需要跟大量节点 命令交互,实际操作起来过于繁琐并且容易遗漏forget节点。建议使用redistrib.rb del-node{host:port}{downNodeId}命令。

redis-trib.rb del-node 127.0.0.1:6379 4fa7eac4080f0b667ffeab9b87841da49b84a6e4 # 从节点6384 id
redis-trib.rb del-node 127.0.0.1:6379 40b8d09d44294d2e23c7c768efc8fcd153446746 # 主节点6381 id

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容