redis 系列(三)- redis主从复制和哨兵模式

普通主从架构(读写分离)

问题

持久化解决了单机redis的数据保存问题,但是redis还是存在以下两个问题:

  1. 假如某天这台redis服务器挂了,redis服务将彻底丧失
  2. redis的读和写都集中到一台机上,如果请求量比较大时,将可能被击溃
解决

为了解决上述两个问题,redis提供了主从架构,在主从架构中,主服务器负责写服务,多台从服务器负责读服务,缓解了单个redis服务器的压力;主服务器将所有数据源源不断的同步到从服务器上,一旦主服务器挂了,还有从服务器可以提供服务,redis服务将不会间断。

特点
  1. 一台主服务器可以连接多台从服务器
  2. 从服务器也可以连接其他redis服务器,作为其他redis服务器的主服务器,从而形成一条链
  3. 主从同步是异步的,从服务器不会阻塞,但是在数据写到从服务器内存的这段期间,从服务器对外提供的还是旧的数据
类型
  1. 全量同步 指主服务器每次与从服务器同步都是同步全部数据。主服务器持久化数据为一个rdb文件,在此期间用缓存区把所有对主服务器的写操作命令存储起来了,然后再rdb传给从服务器,再把储存起来的命令也传过去;从服务器从接收到的rdb文件加载数据,然后再加载传过来的命令。
  2. 部分同步 指主服务器每次与从服务器同步都是只同步增量数据。

原理

  1. 从服务器收到客户端的saveof命令,检查是否存储了主服务器的运行id和复制偏移量。
  2. 如果没绑存储,从服务器发送 psync 1命令和主服务器进行一次全量复制,并且保存主服务器发过来的运行id 和 复制偏移量
  3. 如果有存储了,从服务器发送运行id和复制偏移量,主服务器比较运行id是否一致,复制偏移量是否正常,如果不一致或者不正常,进行全量同步
  4. 如果运行id和复制偏移量正常,那么二者进行增量同步,同步根据传过来复制偏移量,到复制缓存区找到对应的字节,并且把该字节对应的之后的命令都同步过去
操作
  • slaveof master-ip master-port 在从服务器的redis.conf中配置主服务器的host,port
  • slave-read-only yes 从服务器默认只读,这里可改成no为可写(可选)
  • auth 从服务器配置主服务器的密码(可选)
  • requirepass 主服务器配置密码(可选)

从服务器配置好slaveof master-ip master-port 后重启,就可以与主服务器进行同步了。

# 主服务器信息
172.17.0.3:6379> info replication
role:master
connected_slaves:2
# 两个从服务器复制偏移量为4908
slave0:ip=172.17.0.4,port=6379,state=online,offset=4908,lag=1
slave1:ip=172.17.0.5,port=6379,state=online,offset=4908,lag=1
# 主服务器的复制偏移量也为4908
master_repl_offset:4908
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:4907

哨兵模式

作用
  • 不断的检查主从架构中的redis服务中正常运行。
  • 如果出现问题会发信息提示你。
  • 如果主服务器挂了的话,会从从服务器中重新选举一台作为主服务器。
原理
  • 哨兵系统的分类
    哨兵系统可以分为单哨兵和多哨兵;单哨兵是指只有一个哨兵进程,多哨兵是指有多个哨兵进程;单哨兵挂了的话,哨兵系统也就挂了,多哨兵只有一个哨兵挂了不影响哨兵系统继续提供服务。

  • 大概原理
    哨兵也是一个Linux的进程;各个哨兵分布在不同的Linux服务器或者同一个Linux服务器上(一个风险比较大),它们不停的监控redis的主服务器和从服务器与其它的哨兵进程,一旦察觉redis主服务挂了,就会从从服务器中选出一个作为新的主服务器提供服务。

  • 哨兵怎么知道监控其它哨兵和redis服务?
    哨兵每秒钟会向其它哨兵或者redis服务器发送ping命令,根据是否有返回来判断服务是否已经下线。

  • 我们只是在哨兵的配置文件里配置了主服务器信息,但是它怎么知道从服务器信息?
    哨兵每十秒钟会向redis主服务器或者从服务器执行info replication的命令,来确认它们的主从关系。

  • 我们没有配置其他哨兵的地址,哨兵怎么知道其他哨兵地址?
    哨兵每隔两秒就会向redis主节点的sentinel:hello频道发布哨兵对于主节点的判断以及当前哨兵的信息,其它哨兵也会也会如此,并且从中获取所有的哨兵信息。

  • 确认一台redis服务器下线经历了什么流程?
    哨兵不断的PING redis服务器,当发现服务器超过配置的down-after-milliseconds的时间都没有响应,就会认为这台主观下线;这时候哨兵会向其他哨兵发送is-master-down-by-addr命令询问是否可以标记为客观下线,当认为这台redis服务器主观下线的哨兵超过我们配置的quorum(一般设为哨兵数量的一半加1)的值的时候,我们就可以认为这台redis服务器客观下线。为什么还要去询问其他哨兵呢?这是因为哨兵和redis服务器之间没有ping成功也可以能网络之间的问题。

  • 为什么要对哨兵进行领导者选举?
    当确定redis服务器确实挂了以后,哨兵要进行故障转移,并且只能有一个哨兵去完成该操作,所以这时候就要选举出一名哨兵来当此重任。那怎么选举呢?

  1. 哨兵向其它哨兵发送is-master-down-by-addr除了确认是否机器是否可以下线以外,会有发起选举的作用
  2. 其它哨兵收到命令以后,如果如果没有答应其它哨兵的选举请求就会答应该哨兵的请求
  3. 当同意(包括自己)的哨兵个数达到quorum,该哨兵就会成为领导者
  • 怎么完成故障转移?
    当确定原来的redis主服务器已经客观下线以后,就会从从服务器中选出一台作为新的主服务器,选择顺序如下:
  1. 看配置的slave-priority,如果从服务器不相等,返回最高的那台,如果相同看下一步
  2. 看offset,即复制偏移量,如果复制偏移量不同,返回最高那台,如果相同看下一步
  3. 看runid,程序id,runid越低可以看做是越早开启,返回越低那台

确定完是哪台从服务器作为新的主服务器以后,会修改新的从服务器的slaveof与各个哨兵的监控的主服务器的地址和ip。

举个栗子

开启三个redis服务,一主两从;开启三个哨兵;把主服务器关闭掉,查看从服务器是否会产生新的主服务器。

redis配置
# 主服务器 redis-6379.conf
port 6379 
daemonize yes
protected-mode no
logfile "6379.log"
dbfilename "dump-6379.rdb"

# 从服务器 redis-6380.conf
port 6380 
daemonize yes
protected-mode no
logfile "6380.log"
dbfilename "dump-6380.rdb"
slaveof 139.199.168.61 6379

# 从服务器 redis-6381.conf
port 6381 
daemonize yes
protected-mode no
logfile "6381.log"
dbfilename "dump-6381.rdb"
slaveof 139.199.168.61 6379

redis-server redis-6379.conf 启动各个redis服务。

哨兵配置
#sentinel-26379.conf
port 26379
daemonize yes
protected-mode no
logfile "26379.log"
#监控的redis主服务器,最后面的2就是上面提到的quorum,当哨兵响应的个数超过这个数,redis服务器才会被认为是客观下线
sentinel monitor mymaster 139.199.168.61 6379 2
#当超过这个值redis服务器对哨兵的ping不做出响应会被哨兵认为是主观下线
sentinel down-after-milliseconds mymaster 10000

#sentinel-26380.conf
port 26380
daemonize yes
protected-mode no
logfile "26380.log"
sentinel monitor mymaster 139.199.168.61 6379 2
sentinel down-after-milliseconds mymaster 10000

#sentinel-26381.conf
port 26381
daemonize yes
protected-mode no
logfile "26381.log"
sentinel monitor mymaster 139.199.168.61 6379 2
sentinel down-after-milliseconds mymaster 10000

redis-sentinel sentinel-26379.conf 启动各个哨兵服务。

哨兵启动后我们可以查看sentinel-26379.conf:

port 26379
daemonize yes
protected-mode no
logfile "26379.log"
sentinel myid f65e6f01127c838e023f48b73c0f9642548a176d
sentinel monitor mymaster 139.199.168.61 6379 2
# Generated by CONFIG REWRITE
dir "/usr/local/redis-3.2.6"
sentinel down-after-milliseconds mymaster 10000
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel known-slave mymaster 139.199.168.61 6381
sentinel known-slave mymaster 139.199.168.61 6380
sentinel known-sentinel mymaster 10.104.90.159 26381 a8ca7a98c5b462c5c341650386be589cf31a5761
sentinel known-sentinel mymaster 10.104.90.159 26380 eb637934a6e00a26c36fef681cb1e194127b7d2c
sentinel current-epoch 0

会发现哨兵已经把redis的从服务器和其他哨兵加进来了,我们关闭主服务器kill redis主服务器的进程,再查看sentinel-26379.conf

port 26379
daemonize yes
protected-mode no
logfile "26379.log"
sentinel myid f65e6f01127c838e023f48b73c0f9642548a176d
sentinel monitor mymaster 139.199.168.61 6381 2
# Generated by CONFIG REWRITE
dir "/usr/local/redis-3.2.6"
sentinel down-after-milliseconds mymaster 10000
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-slave mymaster 139.199.168.61 6379
sentinel known-slave mymaster 139.199.168.61 6380
sentinel known-sentinel mymaster 10.104.90.159 26381 a8ca7a98c5b462c5c341650386be589cf31a5761
sentinel known-sentinel mymaster 10.104.90.159 26380 eb637934a6e00a26c36fef681cb1e194127b7d2c
sentinel current-epoch 1

我们可以看到监控的主服务器已经切换成 6381而不是之前的6379了。

Java使用
  • Java使用redis的一般做法:
  Jedis jedis = new Jedis("139.199.168.61", 6379);
  System.out.println(jedis.get("hello"));

这种做法最大的弊端在于万一139.199.168.61:6379 这个redis挂了,这个Java应用就废了。

  • 如果我们现在知道了哨兵模式,我们可以改写成以下的做法:
        //声明一个set 存放哨兵集群的地址和端口
        Set<String> sentinels = new HashSet<String>();
        sentinels.add("139.199.168.61:26379");
        sentinels.add("139.199.168.61:26380");
        sentinels.add("139.199.168.61:26381");
        JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", sentinels);
        // 使用sentinelPool获取jedis对象
        Jedis master = sentinelPool.getResource();
        System.out.println(master.get("hello"));
        master.close();
        sentinelPool.destroy();

这样子,当主服务器挂掉以后,哨兵集群会返回给程序新的主服务器地址,保证服务不会挂掉。

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

推荐阅读更多精彩内容