詳細講解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的讀寫分離機制。

發佈了56 篇原創文章 · 獲贊 65 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章