详细讲解Redis主从结构配置以及复制原理(一)

Redis是基于内存的NoSql数据库,同时以其卓越的读写性能闻名业内,并且我在这篇博客Redis持久化机制原理分析与解惑-为什么Redis进行RDB持久化数据时,新起一个进程而不是在原进程中起一个线程中讲过Redis的两种数据持久化方式,但是如果Redis读写压力较大的情况下,将所有的数据都存在一个实例中,这将会大大降低Redis的性能,但是我们可以使用Redis提供的主从复制功能来实现数据冗余和读写分离。

Redis在2.8版本之前,对于从机器每次重连到主都会发送SYNC命令进行全量复制,在2.8版本之后从向主发送PSYNC支持断点续传的复制方式,我们接下来依次看看这两种不同的主从复制方式。

(一)我们先看2.6版本全量主从复制流程:

(1) 从节点起动起来之后主动向主节点发送SYNC命令要求同步数据

(2) 主节点收到SYNC命令之后,fork出一个子进程非阻塞主实例的执行RBD持久化机制,执行完RDB之后将rdb文件发给从节点,

在执行RDB期间主节点将写命令写入缓存,当主节点接到多个从发送SYNC命令之后只执行一次RDB

(3) 从节点收到rdb文件之后载入内存,这个过程从节点可以使用旧数据响应客户端请求,也可以返回一个错误信息

(4) 主节点将缓存中的写命令以Redis命令协议的形式发给从节点


(二)下面先使用版本redis-2.6.16.tar.gz构建一个主从架构

(1)解压 tar zxvf  redis-2.6.16.tar.gz

(2)进入到解压后的文件夹redis-2.6.16 执行make命令

(3)复制redis.conf配置文件 为redis_6379.conf、redis_6380.conf、redis_6381.conf

依次修改内容如下:

#主节点配置文件redis_6379.conf
port 6379
#配置log文件
logfile master_6379.log
#主节点关闭持久化机制
#save 900 1
#save 300 10
#save 60 10000
#内存设置为100M,在实际使用中 这里内存实际数值设置要大于实际所需,原因是要为主写命令缓存区预留出空间
maxmemory 104857600

#从节点配置文件redis_6380.conf
port 6380
#配置log文件
logfile slave_6380.log
maxmemory 104857600
#设置将该节点设置为 6379端口实例的从节点
slaveof 127.0.0.1 6379

#从节点配置文件redis_6381.conf
port 6381
#配置log文件
logfile slave_6381.log
maxmemory 104857600
#设置将该节点设置为 6379端口实例的从节点
slaveof 127.0.0.1 6379
(4)进入到src目录下依次执行如下命令:

nohup ./redis-server ../redis_6379.conf &
nohup ./redis-server ../redis_6380.conf &
nohup ./redis-server ../redis_6381.conf &

(5)我们通过log来看一下主从复制过程

这份是主节点的log



这份是其中一个从节点的log

从主从的log中也可以清晰的看到主从复制的流程,其中从节点我们配置文件中设置了slave-serve-stale-data yes,这样从节点就会非阻塞的方式加载rdb文件,并且使用旧数据响应客户端读请求。


(三)登录客户端查看主从信息

使用如下命令依次登录主从节点

./redis-cli -h 127.0.0.1 -p 6379
./redis-cli -h 127.0.0.1 -p 6381

使用info命令看到主从的信息:

主节点主从复制信息如下图,可以看到主节点中有两个从节点,并且从节点都在线


从节点主从复制信息,记录了主节点的相关信息


(四)使用telnet 查看Redis主节点以Redis命令协议形式发送缓存写命令

这里提供一个连接Redis服务端的Jedis工具类给大家

/**
 * 连接redis服务的工具类
 * @author yujie.wang3
 * @since 09/08/2017
 */
public final class RedisUtil {
    
    //Redis服务器IP
    private static String ADDR = "10.4.36.87";
    
    //Redis的端口号
    private static int PORT = 6379;
   
    //可用连接实例的最大数目,默认值为8;
    //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
    private static int MAX_ACTIVE = 100;
    
    //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
    private static int MAX_IDLE = 20;
    
    //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
    private static int MAX_WAIT = 10000;
    
    private static int TIMEOUT = 10000;
    
    //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
    private static boolean TEST_ON_BORROW = true;
    
    private static JedisPool jedisPool = null;
    
    /**
     * 初始化Redis连接池
     */
    static {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxActive(MAX_ACTIVE);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWait(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);
            jedisPool = new JedisPool(config, ADDR, PORT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 获取Jedis实例
     * @return
     */
    public synchronized static Jedis getJedis() {
        try {
            if (jedisPool != null) {
                Jedis resource = jedisPool.getResource();
                return resource;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    /**
     * 释放jedis资源
     * @param jedis
     */
    public static void returnResource(final Jedis jedis) {
        if (jedis != null) {
            jedisPool.returnResource(jedis);
        }
    }
}

我们使用如下代码写入和读取key

/**
 * 测试Redis主节点以Redis命令协议的形式向从节点发送写命令
 * @author yujie.wang
 * @since 09/08/2017
 */
public class RedisTest {

	public static void main(String[] args) {
		String key0 = "yujie_";
		String value = "";
		for(int i = 0; i <= 10000; i++){
			String key = key0 + String.valueOf(i);
			value = String.valueOf(i);
			//写入key
			addKeys(key, value);
			System.out.println("add key"+ key + " value: "+ value);
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//读取key
			System.out.println("get_Key: " + key + " : "+ getKey(key));
		}
	}
	
	public static String getKey(String key) {
		Jedis client = RedisUtil.getJedis();
		String value = client.get(key);
		RedisUtil.returnResource(client);
		return value;
	}
	
	
	public static void addKeys(String key ,String value){
		Jedis client = RedisUtil.getJedis();
		client.set(key, value);
		RedisUtil.returnResource(client);
	}
}

启动程序之后我们使用telnet 127.0.0.1 6379 访问主节点,并发送sync命令:


从这个过程可以看到 主节点首先传过来一个包含内存中数据的rdb文件,之后会以redis命令协议的形式发送写命令,并且每写一个命令就会发送一个命令。基于此我们可以实现redis的读写分离机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值