Redis搭建读写分离(主从复制)与主备切换(哨兵机制)

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 Redis搭建读写分离(主从复制)与主备切换(哨兵机制),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一、主从复制概述

redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。

通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力。主数据库主要进行写操作,而从数据库负责读操作。

主从复制过程:

1:当一个从数据库启动时,会向主数据库发送sync命令

2:主数据库接收到sync命令后会开始在后台保存快照(执行rdb操作),并将保存期间接收到的命令缓存起来

3:当快照完成后,redis会将快照文件和所有缓存的命令发送给从数据库

4:从数据库收到后,会载入快照文件并执行收到的缓存的命令

二、主从复制配置

准备3个Redis,分别修改redis.conf(Linux) 或 redis.windows-service.conf(windows)

//主Redis的IP及端口 : slaveof ip 6379
slaveof <masterip> <masterport>

//若主Redis启用密码则需配置 : masterauth 123456
masterauth <master-password>

A、以windows为例

配置主从复制

准备3个全新Redis,修改redis.windows-service.conf

Redis(主) :使用默认配置

Redis-2 (从) :port 6380 slaveof 127.0.0.1 6379

Redis-3(从) :port 6381 slaveof 127.0.0.1 6379

启动Redis服务

启动Redis(主) 后分别启动 Redis-2 (从)与Redis-3(从) 。
在这里插入图片描述

验证测试

主Redis写入

127.0.0.1:6379> set name redis
OK
127.0.0.1:6379> get name
"redis"
127.0.0.1:6379>

从Redis-2读取

D:\Program Files (x86)\Redis>redis-cli.exe -p 6380 -h 127.0.0.1
127.0.0.1:6380> keys *
1) "name"
127.0.0.1:6380> get name
"redis"

从Redis-3读取

D:\Program Files (x86)\Redis>redis-cli.exe -p 6381 -h 127.0.0.1
127.0.0.1:6381> keys *
1) "name"
127.0.0.1:6381> get name
"redis"
127.0.0.1:6381>

查看主从复制信息

Redis Info 命令以一种易于理解和阅读的格式,返回关于 Redis 服务器的各种信息和统计数值。

查看主Redis主从复制信息 : info replication

# Replication
# 节点角色
role:master
# 从节点数量
connected_slaves:2
# 从节点状态信息,offset:从节点已复制数据偏移量416 lag:延迟时间
slave0:ip=127.0.0.1,port=6380,state=online,offset=416,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=416,lag=1
# 主节点ID
master_replid:172cc5b955bb4ccb8ec9b4ca6469e4087d03cafa
# 主从发生改变后,该值发生变化
master_replid2:0000000000000000000000000000000000000000
# 主节点写入数据偏移量416
master_repl_offset:416
# 避免全量复制
second_repl_offset:-1
# 缓冲区开启状态 1:开启
repl_backlog_active:1
# 缓冲区大小
repl_backlog_size:1048576
# 从哪里开始写
repl_backlog_first_byte_offset:1
# 当前缓冲去长度
repl_backlog_histlen:416

查看从Redis主从复制信息

# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
# 主从最后一次同步时间 3秒之前
master_last_io_seconds_ago:3
# 主从的同步状态 0:未同步 1:正在同步
master_sync_in_progress:0
# 从节点复制的偏移量500
slave_repl_offset:500
# 从节点选举为主节点的概率
slave_priority:100
# 从节点只读模式 1:开启
slave_read_only:1
# 连接到从节点的信息
connected_slaves:0
master_replid:172cc5b955bb4ccb8ec9b4ca6469e4087d03cafa
master_replid2:0000000000000000000000000000000000000000
# 主节点写入的偏移量500
master_repl_offset:500
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:15
repl_backlog_histlen:486

B、Linux配置

Linux配置主从复制与在Windows环境配置大同小异

公共配置文件

添加redis-common.conf公共配置文件

# 注释掉bind 127.0.0.1 或 放行访问IP限制
bind 0.0.0.0

关闭保护模式,修改为no
protected-mode no


# 端口
# port 6379
# 后台启动
daemonize yes

# 日志存储目录及日志文件名
# logfile "/usr/local/redis/log/redis.log"
# 持久化数据存储在本地的文件名
dbfilename dump.rdb
# 持久化数据存储在本地的路径 rdb数据文件和aof数据文件存储目录
dir /usr/local/program/redis/data

# 设置密码
requirepass 123456
# 从节点访问主节点密码,必须与requirepass一致
masterauth 123456

# aof功能开关,默认为no,关闭状态,根据需求配置
# 每当执行一个改变数据的命令时,这个命令就会被追加到AOF文件的末尾;通过重新执行AOF文件中的命令来达到重建数据的目的
appendonly yes  
# 指定aof文件名称  
appendfilename appendonly.aof  
# 从节点只读不允许写操作,默认yes
replica-read-only yes

私有配置文件

添加3个服务对应的私有配置文件

touch redis-6379.conf
touch redis-6380.conf
touch redis-6381.conf

redis-6379.conf

# 引用公共配置
include /usr/local/redis/conf/redis-common.conf
# 进程编号记录文件
pidfile /var/run/redis-6379.pid
# 进程端口号
port 6379
# 日志记录文件
logfile "/usr/local/redis/log/redis-6379.log"
# 数据记录文件
dbfilename dump-6379.rdb
# 追加文件名称
appendfilename "appendonly-6379.aof"

# 从节点添加该配置
slaveof 127.0.0.1 6380

配置redis-6380.conf与redis-6381.conf

复制redis-6379.conf的内容至redis-6380.conf,redis-6381.conf并且修改其内容,将6379替换即可。

启动Redis服务

/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6379.conf
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6380.conf
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6381.conf

三、主从复制流程

全量复制流程

Slave发送Sync命令到Master

Master收到该命令后执行bgsave命令,生成rdb文件

master生成rdb文件成功后,向Slave发送该文件

Slave接收rdb文件,丢弃旧数据,重新加载RDB文件。

master在生成rdb文件期间,记录期间增量命令到缓冲区,最后Slave从缓冲区获取这些命令

增量复制

增量复制就是Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。

复制过程就是主服务每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令

主从复制异步性

主从复制对于主从Redis服务器时非阻塞的,当从服务器在进行主从同步过程中,主Redis任然可以处理访问请求

主从复制对于从Redis服务器时非阻塞的,从Redis在进行主从复制过程中也可以接收查询请求,只不过这时候从Redis返回的可能是以前的旧数据。

主从其他概念

过期Key

Salve不会让key过期,而是等待master让key过期,master让key过期时会发送一个del命令并传输到所有从服务器。

加速复制

默认情况下,master节点接收Sync命令后执行bgsave操作,将数据先保存到磁盘,如果磁盘性能差,那么写入磁盘会消耗大量性能。在Redis2.8.18时进行了改进,可以设置无需写入磁盘直接发送生成rdb快照给salve,加快复制速度。

# 开启加速复制 默认no
repl-diskless-sync yes

Redis全量添加

全量插入十几万数据测试

逐行插入

    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private GoodsMapper goodsMapper;

    @Test
   	public void test1() {
        List<Goods> goodsList = goodsMapper.findAll();
        long start = System.currentTimeMillis();
        goodsList.forEach(item -> {
            Map<String, Object> goodsMap = BeanUtil.beanToMap(item);
            String key = "goods:" + item.getId();
            redisTemplate.opsForHash().putAll(key, goodsMap);
        });
        long end = System.currentTimeMillis();
        log.info("执行时间:{}", end - start); // 执行时间:54796
    }

Pipeline 管道插入


	@Resource
    private RedisTemplate redisTemplate;
    @Resource
    private GoodsMapper goodsMapper;

      @Test
    public void test2() {
        List<Goods> goodsList = goodsMapper.findAll();
        
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        
        long start = System.currentTimeMillis();
        List<Long> list = redisTemplate.executePipelined((RedisCallback<Long>) connection -> {
            for (Goods goods : goodsList) {
                try {
                    String key = "goods:" + goods.getId();
                    Map<String, Object> goodsMap = BeanUtil.beanToMap(goods);
                    Map<byte[], byte[]> goodsStringMap= Maps.newHashMap();
                    goodsMap.forEach((k, v) -> {
                        goodsStringMap.put(stringRedisSerializer.serialize(k), jackson2JsonRedisSerializer.serialize(v));
                    });
                    connection.hMSet(stringRedisSerializer.serialize(key), goodsStringMap);
                } catch (Exception e) {
                    log.info(goods.toString());
                    continue;
                }
            }
            return null;
        });
        long end = System.currentTimeMillis();
        log.info("执行时间:{}", end - start); //执行时间:25315
    }

四、哨兵机制概述

哨兵机制的主要作用:主备切换

Redis的哨兵(sentinel) 系统用于管理多个 Redis 服务器,该系统执行以下三个任务:

监控(Monitoring):

哨兵(sentinel) 会不断地检查Master和Slave是否运作正常。

提醒(Notification):

当被监控的某个Redis出现问题时, 哨兵(sentinel)可以通过 API 向管理员或者其他应用程序发送通知。

自动故障迁移(Automatic failover):

当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。

哨兵是一个分布式系统

哨兵(sentinel) 是一个分布式系统,可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master.

每个哨兵(sentinel)会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂掉(即”主观认为宕机” Subjective Down,简称sdown).

若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master”彻底死亡”(即:客观上的真正down机,Objective Down,简称odown),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置.

虽然哨兵(sentinel) 为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的Redis服务器,可以在启动一个普通 Redis服务器时通过给定 –sentinel 选项来启动哨兵(sentinel).

五、哨兵工作原理

定时任务

Sentinel内部有3个定时任务

每1秒每个Sentinel对其他Sentinel和Redis节点执行PING操作,即监控
每2秒每个Sentinel通过master节点的channel交换信息,即发布与订阅(Publish、Subscribe)
每10秒每个Sentinel会对master和Slave执行Info命令,获取统计信息

主观下线

单个Sentinel实例对服务器做出下线判断,即单个Sentinel由于接收不到订阅、网络不同等情况认为某个服务下线

客观下线

多个Sentinel实例对同一个服务器做出下线判断,并且通过命令相互交流后,得出服务器下线的判断,然后开始failover。

仲裁

仲裁是指配置文件中的quorum选项,quorum的值一般设置为Sentinel个数的1/2+1,如3个Sentinel就设置为2.

六、Sentinel哨兵配置

Linux环境下修改sentinel.conf配置文件。windows环境下,在redis目录中创建一个sentinel.conf文件。

需注意的是,至少3个Sentinel实例,产生大于1/2的结果

新增Redis服务

在上面主从复制基础上,新增一个新的Redis

Redis-4(从):port 6382 slaveof 127.0.0.1 6379

添加哨兵配置

Redis根目录新建sentinel.conf配置文件

# 当前Sentinel服务运行的端口
port 26379

# 哨兵监听的主服务器IP端口号 选举次数 主实例判断为失效至少需要1Sentinel进程的同意,只要同意Sentinel的数量不达标,自动failover就不会执行
sentinel monitor  mymaster  127.0.0.1 6379 1

# 3s内mymaster无响应,则认为mymaster宕机了
# 只有一个Sentinel进程将实例标记为主观下线并不一定会引起实例的自动故障迁移:只有在足够数量的Sentinel都将一个实例标记为主观下线之后,实例才会被标记为客观下线,这时自动故障迁移才会执行
sentinel down-after-milliseconds mymaster 3000

# 如果10秒后,mysater仍没启动过来,则启动failover
sentinel failover-timeout mymaster 10000

# 执行故障转移时, 最多有1个从服务器同时对新的主服务器进行同步
sentinel parallel-syncs mymaster 1

# 哨兵监听需要密码认证
#sentinel auth-pass mymaster 123456

# 线程守护
daemonize no

# 日志路径
logfile "D:/sentinel.log"

启动Redis服务

分别启动Redis(主),Redis-2(从),Redis-3(从),Redis-4(从)

启动哨兵服务

D:\Program Files (x86)\Redis -4>redis-server.exe sentinel.conf --sentinel

查看sentinel的状态

D:\Program Files (x86)\Redis>redis-cli.exe -p 26379 -h 127.0.0.1
127.0.0.1:26379> info
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=3,sentinels=1

Redis(主)–宕机

当Redis(主)宕机,Sentinel会通过选举(算法)机制,从Salve中选出一个作为新Master。

停止Redis(主)后挨着查看每个从Redis,发现Redis-3(从)已经切换成master了

D:\Program Files (x86)\Redis>redis-cli.exe -p 6381 -h 127.0.0.1
127.0.0.1:6381> info
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6382,state=online,offset=21452,lag=0
slave1:ip=127.0.0.1,port=6380,state=online,offset=21452,lag=0
master_replid:fcadd2d0ff34ebedf8404acb3a5186622776139d
master_replid2:f3b4792067d4b4fee22f6490a1251887df8d722c
master_repl_offset:21452
second_repl_offset:16012
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:21452

Redis(主)–重新启动

查看主动复制信息,发现原来的Redis(主)由主节点变为了从节点
# Replication
role:slave
master_host:127.0.0.1
master_port:6381
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:68256
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:fcadd2d0ff34ebedf8404acb3a5186622776139d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:68256
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:65532
repl_backlog_histlen:2725

节点管理

添加Sentinel

添加单个Sentinel时,只需要启动配置 sentinel monitor masterName监视当前活动主服务器的新Sentinel即可。

添加多个Sentinel时,建议一个一个添加,添加结束后使用命令SENTINEL MASTER mastername检查所有Sentinel是否已完全获取到所有Master的信息

删除Sentinel
Sentinel不会完全清除已添加过的Sentinel信息,因此要移除一个Sentinel需要遵循以下步骤:

停止要删除的Sentinel进程

SENTINEL RESET * 向多有其它Sentinel实例发送命令

执行命令 SENTINEL MASTER mastername检查每个Sentinel显示哈哈的Sentinel数量是否一致

故障迁移一致性

Redis使用分布式一致性算法Raft算法,保证了故障迁移一致性。在同一个周期中不会产生多个领头者,也就不会产生多个迁移,从而保证了故障迁移一致性。

TILT模式

TILT模式就是当前服务器压力大或其他原因导致不能获取系统时间,没有有效的做出命令回复的时间间隔判断,那么就会进入TILT保护模式,当该模式正常运行30秒钟,期间还是无法获取系统时间则延迟该模式。

项目中使用Sentinel

配置文件配置

  # Redis
  redis:
    # port: 6379
    # host: 127.0.0.1
    timeout: 3000
    database: 1
    password: 123456
    sentinel:
      master: mymaster
      nodes: 192.168.30.25:26379,192.168.30.26:26379,192.168.30.27:26379

配置类方式

@Component
public class RedisConfig {
    @Bean
    public RedisConnectionFactory connectionFactory(){
        RedisSentinelConfiguration configuration=new RedisSentinelConfiguration();
        configuration.setMaster("mymaster");
        configuration.sentinel("192.168.30.25",26379);
        configuration.sentinel("192.168.30.26",26379);
        configuration.sentinel("192.168.30.27",26379);
        configuration.setDatabase(1);
        configuration.setPassword("123456");
        //return new JedisConnectionFactory(configuration);
        return new LettuceConnectionFactory(configuration);
    }
}

Linux环境的Sentinel哨兵配置

添加公共配置文件

从redis解压目录下复制sentinel.conf至/usr/local/redis/conf

重命名为:sentinel-common.conf (哨兵公共配置文件)
# 当前Sentinel服务运行的端口
# port 26379

# 哨兵监听的主服务器IP端口号 选举次数 主实例判断为失效至少需要1Sentinel进程的同意,只要同意Sentinel的数量不达标,自动failover就不会执行
sentinel monitor  mymaster  127.0.0.1 6379 1

# 3s内mymaster无响应,则认为mymaster宕机了
# 只有一个Sentinel进程将实例标记为主观下线并不一定会引起实例的自动故障迁移:只有在足够数量的Sentinel都将一个实例标记为主观下线之后,实例才会被标记为客观下线,这时自动故障迁移才会执行
sentinel down-after-milliseconds mymaster 3000

# 在该配置值内未能完成failover操作(故障时master/slave自动切换),则认为本次failover失败
sentinel failover-timeout mymaster 10000

# 执行故障转移时, 最多有1个从服务器同时对新的主服务器进行同步
sentinel parallel-syncs mymaster 1

# 哨兵监听需要密码认证
#sentinel auth-pass mymaster 123456

# 守护线程,后台运行
daemonize yes

# 日志路径
# logfile "usr/local/redis/logs"

添加私有配置文件

添加3个哨兵的私有配置文件

touch sentinel-26379.conf
touch sentinel-26380.conf
touch sentinel-26381.conf

sentinel-26379.conf

# 引用公共配置
include /usr/local/redis/conf/sentinel-common.conf
# 进程端口号
port 26379
# 进程编号记录文件
pidfile /var/run/sentinel-26379.pid
# 日志记录文件(为了方便查看日志,先注释掉,搭好环境后再打开)
logfile "usr/local/redis/logs/sentinel-26379.log"

配置sentinel-26380.conf与sentinel-26381.conf

复制 sentinel-26379.conf 的内容至 sentinel-26380.conf , sentinel-26381.conf 并且修改其内容,将26379替换即可

启动Redis服务与哨兵服务

启动3个redis服务

/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6379.conf
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6380.conf
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis-6381.conf

启动3个哨兵服务

/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel-26379.conf
/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel-26380.conf
/usr/local/redis/bin/redis-sentinel /usr/local/redis/conf/sentinel-26381.conf

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137043.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!