美文网首页
Redis 主从复制

Redis 主从复制

作者: 右耳菌 | 来源:发表于2022-09-01 22:31 被阅读0次

    1. 主从复制介绍

    1.1 什么是主从复制?
    1.2 为什么要主从复制?
    • redis-server 单节点故障
    • 单节点QPS有限
    1.3 主从复制应用场景分析
    • 读写分离场景,规避redis单机瓶颈
    • 故障切换,master出问题后还有slave节点可以使用。

    2. 搭建主从复制

    主Redis Server以普通模式启动,主要是启动从服务器的方式

    2.1 第一种方式:命令行
    #连接需要实现从节点的redis,执行下面的命令
    slaveof [ip] [port]
    
    2.2 第二种方式:redis.conf配置文件
    # 配置文件中增加
    slavepof [ip] [port]
    # 从服务器是否只读(默认yes)
    slave-read-only yes
    
    2.3 退出主从集群的方式
    slaveof no one
    

    注意: 因为文化差异的原因,新的版本可能会将slaveof变为replicaof,但是其实是一样的。


    3. 检查主从复制

    • master


      网上的例子

      实际运行的例子

    192.168.1.7:6379> info replication
    # Replication
    role:master
    connected_slaves:0
    master_replid:4fa1246eb96e7ad381cd21ab0f1f5a267d1a1cec
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:210
    second_repl_offset:-1
    repl_backlog_active:1
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:1
    repl_backlog_histlen:210
    
    • slave


      网上的例子

      实际运行的例子

    192.168.1.7:6378> info replication
    # Replication
    role:slave
    master_host:192.168.1.7
    master_port:6379
    master_link_status:down
    master_last_io_seconds_ago:-1
    master_sync_in_progress:0
    slave_repl_offset:1
    master_link_down_since_seconds:1662020523
    slave_priority:100
    slave_read_only:1
    connected_slaves:0
    master_replid:101535bfbefc8c9858ff46bad42b8439c87295eb
    master_replid2:0000000000000000000000000000000000000000
    master_repl_offset:0
    second_repl_offset:-1
    repl_backlog_active:0
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:0
    repl_backlog_histlen:0
    (3.74s)
    

    注意: 如果从服务器尝试复制数据时,提示以下错误,那么可能是缺少了文件的读取权限,需要使用sudo 或者 切换到 root用户启动!

    Opening the temp file needed for MASTER <-> REPLICA synchronization: Permission denied
    

    4. 主从复制流程

    1. 从服务器通过psync命令发送服务器已有的同步进度(同步源ID、同步进度offset)
    2. master收到请求,同步源为当前master,则根据偏移量增量同步
    3. 同步源非当前master,则进入全量同步: master生成rdb,传输到slave,加载到slave内存

    5. 主从复制核心知识

    • Redis默认使用异步复制,slave和master之间异步地确认处理的数据量
    • 一个master可以拥有多个slave
    • slave可以接受其他slave的连接。slave可以有下级sub slave
    • 主从同步过程在master侧是非阻塞的
    • slave初次同步需要删除旧数据,加载新数据,会阻塞到来的连接请求

    6. 主从复制应用场景

    • 主从复制可以用来支持读写分离
    • slave服务器设定为只读,可以用在数据安全的场景下。
    • 可以使用主从复制来避免master持久化造成的开销。master关闭持久化,slave配置为不定期保存或是启用AOF。(注意:重新启动的master程序将从一个空数据集开始,如果一个slave试图与它同步,那么这个slave也会被清空。)

    7. 主从复制的注意事项

    • 读写分离场景
      1.数据复制延时导致读到过期数据或者读不到数据(网络原因、slave阻塞)
      2.从节点故障(多个client如何迁移)

    • 全量复制情况下
      1.第一次建立主从关系或者runid不匹配会导致全量复制
      2.故障转移的时候也会出现全量复制

    • 复制风暴
      1.master故障重启,如果slave节点较多,所有slave都要复制,对服务器的性能,网络的压力都有很大影响。
      2.如果一个机器部署了多个master

    • 写能力有限
      1.主从复制还是只有一台master,提供的写服务能力有限

    • matser故障情况下
      1.如果是master无持久化,slave开启持久化来保存数据的场景,建议不要配置redis自动重启。
      2.启动redis自动重启,master启动后,无备份数据,可能导致集群数据丢失的情况

    • 带有效期的key
      1.slave不会让key过期,而是等待master让key过期
      2.在Lua脚本执行期间,不执行任何key过期操作


    8. 例子

    首先记得先把lettuce依赖加入pom

            <dependency>
                <groupId>io.lettuce</groupId>
                <artifactId>lettuce-core</artifactId>
                <version>${lettuce.version}</version>
            </dependency>
    

    1. 非读写分离方式
    创建ReplicationRedisAppConfig.java

    package cn.lazyfennnec.cache.redis;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Profile;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.core.StringRedisTemplate;
    
    @Configuration
    @Profile("replication") // 主从模式
    class ReplicationRedisAppConfig {
    
        @Bean
        public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
            StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(redisConnectionFactory);
            return stringRedisTemplate;
        }
    
    
        @Bean
        public LettuceConnectionFactory redisConnectionFactory() {
            // 假设master:192.168.100.241    slave:192.168.100.242
            // 默认slave只能进行读取,不能写入
            // 如果你的应用程序需要往redis写数据,建议连接master
            // 其实本地的redis都是在同一个服务器上的,分别为
            // 1. master 192.168.1.7 6379
            // 2. slave  192.168.1.7 6378
            return new LettuceConnectionFactory(new RedisStandaloneConfiguration("192.168.1.7", 6379));
        }
    }
    

    2. 读写分离的方式
    创建ReplicationRWRedisAppConfig.java

    package cn.lazyfennec.cache.redis;
    
    import io.lettuce.core.ReadFrom;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Profile;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.core.StringRedisTemplate;
    
    @Configuration
    @Profile("replication-rw") // 主从 - 读写分离模式
    class ReplicationRWRedisAppConfig {
    
        @Bean
        public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
            StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(redisConnectionFactory);
            return stringRedisTemplate;
        }
    
        @Bean
        public LettuceConnectionFactory redisConnectionFactory() {
            System.out.println("使用读写分离版本");
            LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
                    .readFrom(ReadFrom.SLAVE_PREFERRED)
                    .build();
            // 此处
            RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration("192.168.1.7", 6378);
            return new LettuceConnectionFactory(serverConfig, clientConfig);
        }
    }
    

    3. Service实现类

    package cn.lazyfennec.cache.redis;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Service;
    
    @Service
    public class ReplicationExampleService {
        @Autowired
        private StringRedisTemplate template;
    
        public void setByCache(String userId, String userInfo) {
            template.opsForValue().set(userId, userInfo);
        }
    
        public String getByCache(String userId) {
            return template.opsForValue().get(userId);
        }
    }
    

    4. 测试代码

    • 非读写分离方式
    package cn.lazyfennec.cache.redis;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    @ActiveProfiles("replication") // 激活主从复制的配置
    public class ReplicationTests {
        @Autowired
        ReplicationExampleService replicationExampleService;
    
        @Test
        public void setTest() {
            replicationExampleService.setByCache("neco", "hahhhhh");
        }
    }
    
    • 读写分离的方式
    package cn.lazyfennec.cache.redis;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    @ActiveProfiles("replication-rw") // 激活主从集群-读写分离的配置
    public class ReplicationRWTests {
        @Autowired
        ReplicationExampleService replicationExampleService;
    
        @Test
        public void setTest() {
            replicationExampleService.setByCache("neco", "xxxx");
            String result = replicationExampleService.getByCache("neco");
            System.out.println("从缓存中读取到数据:" + result);
        }
    }
    

    如果觉得有收获,欢迎点赞和评论,更多知识,请点击关注查看我的主页信息哦~

    相关文章

      网友评论

          本文标题:Redis 主从复制

          本文链接:https://www.haomeiwen.com/subject/wcrsnrtx.html