Redis——分析主从复制,哨兵,cluster,分区,代理 (转)

转自: https://www.it610.com/article/1297347757631152128.htm

转自: https://www.it610.com/article/1297347757631152128.htm

转自: https://www.it610.com/article/1297347757631152128.htm

redis单实例可能会有啥问题

  1. 单点故障
  2. 容量有限
  3. 并发压力

单实例问题的解决方案:AKF

https://www.jianshu.com/p/d08d0c14810f

  • X轴: 部署多节点,每个节点都有全量的数据,可以主从备份,读写分离.解决单点故障的问题,同时也缓解一些压力,无法解决容量的问题;
  • Y轴: 从业务功能上分库,比如用户信息独立一个库,商品信息独立一个库,使用多个实例; 每个Y轴上的节点可以有自己的X轴;
  • Z轴: 按优先级,比如地域划分,有点类似于MySQL的横向分表;比如商品信息太多了,将编号1-1w的商品放入一个库,1w-2w放入另一个库;
    Y轴和Z轴的思想就是从业务上分隔数据,让一个库中的数据少一些,从而把压力平均到多个实例上;Y和Z轴有点客户端sharding的感觉
    Redis(五)redis的高可用理论推导,主从复制,哨兵,cluster,分区,代理,实操twemproxy/Predixy/cluster_第1张图片

多实例的数据一致性的问题

很多事情都是有两面性的,AKF会带来数据一致性的问题.

以主从备份为例,一个主节点,两个备份节点;
(这里说的就是主从模式,但是现在好像不让说slave这个词了,下面说的备份节点其实就是以前的slave节点)

  • 强一致性(破坏可用性)
    所有节点阻塞,直到数据全部一致;
    假设其中一个备份节点挂掉了,或者网络延迟,就会导致整个服务不可用,破坏可用性.
    这个成本太高,几乎不会这么做;
    反过来想,为什么要一变多,就是为了保证可用性
  • 容忍数据丢失一部分
    主节点写入数据后 马上返回成功响应;然后异步方式,把数据写到备份节点,同步数据
    假设备份节点挂了,就可能丢掉一部分数据
  • 最终一致性
    同步数据时引入消息中间件,比如Kafka
    主节点写入数据后 马上返回成功响应;然后把消息(同步阻塞)写入Kafka,备份节点从Kafka中读取信息,从而同步数据
    这要求Kafka高可用,响应速度够快
    这样即使备份节点挂掉了,最终仍会从Kafka中读取数据.
    其实这里有个小问题:
    当客户端读取数据时,假设备份节点还没来及消费同步数据,就有可能取到错误的数据

CAP

http://www.ruanyifeng.com/blog/2018/07/cap.html
Consistency一致性,Availability可用性,Partition Tolerance分区容忍性 这仨条件不能同时满足,最多同时满足两点.

先看 Partition tolerance,中文叫做"分区容错"。
大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务器放在美国,这就是两个区,它们之间可能无法通信。

一般来说,分区容错无法避免,因此可以认为 CAP 的 P 总是成立。CAP 定理告诉我们,剩下的 C 和 A 无法同时做到。

一致性和可用性,为什么不可能同时成立?答案很简单,因为可能通信失败(即出现分区容错)。
假设两个节点G1,G2,向G1写后,向G2读
如果保证 G2 的一致性,那么 G1 必须在写操作时,锁定 G2 的读操作和写操作。只有数据同步后,才能重新开放读写。锁定期间,G2 不能读写,没有可用性不。
如果保证 G2 的可用性,那么势必不能锁定 G2,所以一致性不成立。

一般来说,网页的更新不是特别强调一致性。短时期内,一些用户拿到老版本,另一些用户拿到新版本,问题不会特别大。当然,所有人最终都会看到新版本。所以,这个场合就是可用性高于一致性。

主从复制的模式,我们需要让master节点高可用;
当master节点挂掉后,从replica节点中选举一个作为新的master节点,也就是自动故障转移;
由程序去实现监控并故障转移,只要是程序就有单点故障问题,所以需要一变多的集群;
假设有3个监控节点,这又涉及到数据一致性的问题,所以就以"大多数"(过半)的观点为准,因为有可能是监控节点自己有网络问题

为啥要"大多数",“过半”:
如果只看一个节点,统计不够准确,有可能不同的节点有不同的观点,因为网络分区的问题,就可能请求到不一样的数据,发生脑裂;
如果看"大多数",只要过半的节点的观点是一致的,就认为这是对的结果,这就是分区容忍性.

n台机器组成集群的话,"过半"的节点就是n/2 + 1, 即允许 n/2 - 1 台机器挂掉;所以一般n是个基数
3台和4台组成的集群,都只允许一台挂掉,但是4台机器中挂掉1台的几率要大一些.

复制(Redis Replication)

http://redis.cn/topics/replication.html

help slaveof 会提示,5.X开始,用replicaof代替
所以,help replicaof
常用命令:

  • replicaof ip port 追随某个master节点
  • replicaof no one 谁也不追,自己当master节点

可以直接在从节点客户端中, replicaof ip port 指定主节点 (更多时候是通过配置文件)
指定主节点后,首次同步时,会先把自己的数据删掉,然后从主节点拉取RDB重新加载;
默认从节点不能写数据,只能读.

如果主节点挂了,可以人工的从replica节点中选一个作为master节点,然后其他节点重新追随新的master节点,实现故障转移.

如果从节点挂了,又启动回复时,是怎么恢复数据呢,重新从主节点拉取RDB吗?
如果从节点没有开启AOF,使用RDB,则不会从主节点全量拉取RDB,因为本地的RDB文件记录了masterID,可以增量同步
如果从节点开启了AOF,那么会重新拉取RDB,全量同步;因为AOF中的RDB部分,没有记录masterID,不知道出于啥考虑

master节点可以知道有哪些replica节点

配置文件

  • replicaof 指定主节点
  • masterauth 主节点的密码
  • replica-serve-stale-data yes/no 当从节点和主节点丢失联系,或者正在同步数据时,是否可以继续提读服务,默认是yes,可能读到老数据
  • replica-read-only yes 从节点是否只读,默认yes
  • repl-diskless-sync no 当有新的从节点,或者从节点重新连接时,不能增量复制了,需要一次全量同步(full synchronization),通过传输RDB实现;传输RDB时可以先写到磁盘上,然后再传输(yes);或者不用磁盘,直接从内存中传输(no);默认是no,即使用磁盘.
  • repl-backlog-size 1mb 主从之间日常会增量的同步数据,redis内部维护一个buffer队列,从节点通过上次更新的offset判断,只拉取最新的增量修改;这个参数指定队列的大小.如果buffer太小,从节点找不到上次的offset了,就会全量同步
  • min-replicas-to-write 3 最少有几个节点在线,否则就拒绝写操作,有点"同步阻塞" 强一致性的感觉;默认注释掉
  • min-replicas-max-lag 10 从节点的心跳间隔大于这个数时,就认为不在线了,单位是秒;默认注释掉

高可用性(High Availability) 哨兵Sentinel

http://redis.cn/topics/sentinel.html
哨兵负责监控,提醒,自动故障转移;
一套哨兵可以监控多套主从集群

可以通过reisid-sentinel启动哨兵
redis-sentinel 和 sentinel.conf 在redist源码src目录可以找到
配置文件,重点看这个:
sentinel monitor mymaster 127.0.0.1 6379 2 监控主节点,并指定2票通过

哨兵其实是一个特殊的redis-server,可以启动redis-server时--sentinel 来启动哨兵

哨兵只需要知道master节点信息即可,从master里可以知道从节点信息,通过发布订阅(pubsub)来发现其他哨兵
可以通过PSUBSCRIBE * 订阅所有channel来观察一下

哨兵通过监听主获取其他机器信息,然后通过发布订阅获取其他哨兵
在这里插入图片描述

分区

http://redis.cn/topics/partitioning.html

客户端的sharding方式(缓存)

modula,random,ketama 参考https://github.com/twitter/twemproxy
这三种都不适合做数据库,适合做缓存

按模块/业务拆分

很多人可能不认为这是分区,但是我觉得从宏观上来看,这也是sharding的思想
把不同的业务存在不同的库中,从而减少每个库的压力(AKF中的Y轴)

将数据 hash + 取模 (modula)

弊端:取模的数是固定,去觉得当下redis的实例数,不易扩展

生产者random lpush,消费者rpop (类似kafka的partition) (random)

客户端a作为生产者,往N个redis实例中随机lpush数据;
客户端b作为消费者,从N个redis实例中不停rpop取数据
此时,这个list的key就相当于kafka的topic;只是Kafka是基于磁盘的,可以重复消费,redis不行

一致性hash,规划一个哈希环 (只适合用于缓存) (ketama)

假设刚开始有两个机器作为redis节点;
我们规划一个哈希环,设这个环的offset范围是0~232
把两个节点分别hash,几位n1hash,n2hash;
当有数据进来时,对数据hash,得到的值和n1hash,n2hash比较,和谁的差小就存在哪里;
这就是最简单的哈希环.

当新增一个节点node3时,可能会有一部分的数据不能命中,这时可以去数据库里面查一次,再放入node3;
其他节点有重复数据也无所谓,这种方式本就适合于缓存,一般都会用LRU回收掉

为了防止数据倾斜,很多数据集中在一个节点的情况,可以设置很多虚拟节点;
即使只有两个物理节点,让他们各自的ip加上一定数字 映射出多台机器;从而使两台物理节点在环上可以交替负责,很大概率防止数据倾斜.
Redis(五)redis的高可用理论推导,主从复制,哨兵,cluster,分区,代理,实操twemproxy/Predixy/cluster_第2张图片

客户端sharding的缺点

每个客户端都要连接所有的redis-server
假设有很多个客户端,将对server造成很大压力
Redis(五)redis的高可用理论推导,主从复制,哨兵,cluster,分区,代理,实操twemproxy/Predixy/cluster_第3张图片

所以就想,能不能像nginx那样,通过反向代理搞定:
其实这相当于时把sharding逻辑算法,从客户端迁移到了代理层,代理层实现modula,random,ketama
代理层时无状态的,满足了无状态才方便下面增加代理层节点
如果考虑代理性能,代理也可以做个集群
Redis(五)redis的高可用理论推导,主从复制,哨兵,cluster,分区,代理,实操twemproxy/Predixy/cluster_第4张图片

或者加入LVS,keepalived:
无论server端多复杂,对于客户端来说,只要请求那个LVS即可
Redis(五)redis的高可用理论推导,主从复制,哨兵,cluster,分区,代理,实操twemproxy/Predixy/cluster_第5张图片
但是这样的话,复杂度就太大了;
不要因为技术而技术.遵循KISS原则,满足需求的情况下,越简单越好.

cluster

https://redis.io/topics/cluster-tutorial
这是官方的解决方案

预分区
这个有点类似于虚拟节点,举个例子:
刚开始一共两个redis节点,但是预分出10个分区(槽位),平均分给两个节点;
当新添加一个节点时,从这两个节点中划分一些槽位给新节点,进行增量的数据传输(数据迁移),即可快速的完成扩展工作.
数据迁移的同时也支持修改,类似于主从备份的首次全量同步,和后续的增量buffer同步

cluster属于"无主"模式,也没有代理
客户端连接的时候,可以连接cluster中任意一个server节点,每个节点都知道各个槽位和其他节点的映射关系
节点收到请求后,会做个hash运算,找到对应的槽位,然后从映射关系找到对应的节点;
如果数据不在本节点上,给客户端返回一个"重定向"的响应,由客户端再次请求对应的节点

数据分治,分开后,有些东西很难实现:聚合操作,事务
redis为了追求速度,没有移动数据的功能,所以没有实现这些玩意;(redis理念:计算向数据移动)
如果一个事务需要的数据都在一个节点上,那就可以正常运行;所以可以通过hash tag解决这些问题
客户端通过给key设置hash tag,然后server端通过hash tag 做hash运算,相同的hash tag一定会落在同一个节点上.

实操

https://blog.csdn.net/rebaic/article/details/76384028
无论是为了解决redis的高可用问题、还是为了可扩展性、或者是为了维护方便,用一款redis代理都是上佳的选择。在github上有众多开源的redis代理,其中最流行的有这几个:(貌似predixy表现最好,但是作者比较神秘)

  • predixy : https://github.com/joyieldInc/predixy
  • twemproxy : https://github.com/twitter/twemproxy
  • codis
  • redis-cerberus
    Redis(五)redis的高可用理论推导,主从复制,哨兵,cluster,分区,代理,实操twemproxy/Predixy/cluster_第6张图片

twemproxy

按照github的readme即可,https://github.com/twitter/twemproxy

这里注意有几个坑:
autoreconf -fvi && ./configure needs automake and libtool to be installed
需要先安装automake 和 libtool ,直接yum install 即可

然后执行autoreconf -fvi可能会提示你autoreconf 版本太低,需要618以上版本;
autoreconf是属于autoconf的命令,yum search autoconf发现最高到autoconf213.noarch,这就需要更换yum镜像了
从阿里云镜像上找到Epel 镜像,按提示操作后,yum clean all
yum search autoreconf 后发现多了个autoconf268,然后yum install autoconf268
执行autoreconf -fvi时 把autoreconf替换为autoreconf268

然后编译完成后,发现scrips下有个nutcracker.init,这就是启动脚本, 把它拷贝到/etc/init.d/twemproxy
然后 chmod +x /etc/init.d/twemproxy
然后准备启动需要的配置文件:
查看启动脚本,它默认读取/etc/nutcracker/nutcracker.yml,
我们就创建个文件夹 mkdir /etc/nutcracker
然后进入源码目录的conf目录,把配置文件复制过去 cp ./nutcracker.yml /etc/nutcracker/

准备可执行程序,默认prog=“nutcracker”,所以我们把编译后的可执行程序放入环境变量目录
进入编译后的源码目录/src,cp nutcracker /usr/bin

然后修改配置文件,vi /etc/nutcracker/nutcracker.yml (修改前做个备份),我们配置连接自己的6379 和 6380

然后启动它,service twemproxy start
然后客户端连接代理的端口 redis-cli -p 22121 ,随机写一些数据,代理会把数据分配到两个实例上
代理层因为数据分治了,不支持keys * 和 事务(watch,multi)
然后直接连接6379和6380,看看怎么分配的

Predixy

https://github.com/joyieldInc/predixy/blob/master/README_CN.md
编译需要C++11 compiler,centOS6.X上比较麻烦
可以直接使用他编译好的 https://github.com/joyieldInc/predixy/releases

predixy支持Redis Sentinel和Redis Cluster来使用redis,一个配置里这两种形式只能出现一种。
哨兵模式中,
SentinelServerPool P{
Sentinels {
}
Group xxx {
}
}
一套Sentinel可以监控多套主从的,多套出从就要分多个Group;Group的名要和Sentinel监控的master逻辑名一致
Predixy可以 将数据,根据指定算法 分发到各个Group中
Predixy的事务只在单Group下支持

改好配置文件后,启动: ./predixy …/conf/predixy.conf

然后客户端连接predixy代理 redis-cli 7617
set {xx}k1 v1
set {xx}k2 v1
会把这俩分发到同一个Group中,但是只在单Group下支持事务

cluster

https://redis.io/topics/cluster-tutorial
cd {redis源码目录}/utils/create-cluster,
这里有个脚本create-cluster和README
create-cluster脚本默认NODES=6,REPLICAS=1 即三套主从,每套一个replica节点
直接./create-cluster start
./create-cluster create 自动分槽位和建立主从关系,这个会在单机上分配,可以做实验用

启动redis-cli时,如果直接redis-cli -p 30001,
然后set k1 v1会提示 (error) MOVED 12706 127.0.01:30003 让我们换到30003的节点上做操作

可以redis-cli -c -p 30001 (-c cluster模式),然后set k1 v1就没问题了,会自动跳转
这时可以watch,multi等操作,但是如果执行的操作不在一套主从上,exec时会报错
这事可以使用hash-tag, set {oo}k1 v1,set {oo}k2 v2,可以保证k1 k2在一套主从上,这时事务就不会报错了

实验完毕后,可以./create-cluster stop , ./create-cluster clean

生产中不会用./create-cluster create自动分配,因为都是分布在不同机器上的,所以可以在./create-cluster start后,手动分配,redis-cli --cluster help查看帮助命令
redis-cli --cluster create ip:port ip:port ip:port… ip:port --cluster-replicas 1
create前需要启动我们需要的redis-server,使用这个脚本可以不修改配置文件.
还可以 add-node/del-node 添加/删除节点,
redis-cli --cluster reshard ip:port , 新加节点后,从一个或多个或所有M节点中, 分一部分槽位给另一个M节点;不能控制具体哪个槽位;
redis-cli --cluster info ip:port 查看一下各M节点的槽位分配情况
redis-cli --cluster check ip:port 查看槽位分配详情,和个节点的ID
rebalance,

目前应该是需要手动添加节点,分配槽位

目前对cluster最有好感,毕竟是官方的解决方案,而且更好的支持事务.

cluster配置文件

如果不想使用create-cluster 脚本的话(start),也可以开启cluster配置文件后,和普通redis-server一样启动.

    • cluster-enabled yes
    • cluster-config-file nodes.conf
    • cluster-node-timeout 5000
posted @ 2021-05-10 20:01  会飞的斧头  阅读(255)  评论(0编辑  收藏  举报