一文輕鬆搞懂redis叢集原理及搭建與使用

SnailClimb發表於2018-04-17

今天早上由於zookeeper和redis叢集不在同一虛擬機器導致出了點很小錯誤(人為),所以這裡總結一下redis叢集的搭建以便日後所需同時也希望能對你有所幫助。 筆主這裡使用的是Centos7.如果你碰到任何問題都可以來問我,留言或者加我微信:bwcx9393.

關於Linux的一些資料 連結:pan.baidu.com/s/1Opgu6kQe… 密碼:8yk4

一 redis的安裝

Redis是c語言開發的。 安裝redis需要c語言的編譯環境。如果沒有gcc需要線上安裝:yum install gcc-c++

第一步:獲取原始碼包:wget http://download.redis.io/releases/redis-3.0.0.tar.gz

第二步:解壓縮redis:tar zxvf redis-3.0.0.tar.gz

第三步:編譯。進入redis原始碼目錄(cd redis-3.0.0)。執行 make

第四步:安裝。make install PREFIX=/usr/local/redis

PREFIX引數指定redis的安裝目錄。一般軟體安裝到/usr目錄下

這樣Redis就成功裝在了我們的usr/local/redis目錄下。

第五步:設定後臺啟動: [root@localhost redis-3.0.0]# cp redis.conf /usr/local/redis/bin/ (把/root/redis-3.0.0/redis.conf複製到/usr/local/redis/bin目錄下)

修改配置檔案:把daemonize後面的引數改為yes

修改配置檔案:

測試啟動:[root@localhost bin]# ./redis-server redis.conf

檢視redis程式: [root@localhost bin]# ps aux|grep redis

二 redis叢集的搭建

2.1 redis叢集(redis-cluster)原理

3.0版本之前的redis是不支援叢集的,3.0版本之前想要搭建redis叢集的話需要中介軟體來找到存值和取值的對應節點。

3.0版本以後的redis叢集架構圖:

3.0版本以後的redis叢集架構圖

那麼這是如何實現的呢???

Redis 叢集中內建了 16384個雜湊槽,當需要在 Redis 叢集中放置一個 key-value 時,redis 先對 key 使用 crc16 演算法算出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的雜湊槽,redis 會根據節點數量大致均等的將雜湊槽對映到不同的節點。

根據下圖應該更容易理解了。(圖片來源:http://www.cnblogs.com/liyasong/p/redis_jiqun.html)

叢集原理

redis叢集投票機制

redis叢集中有多臺redis伺服器不可避免會有伺服器掛掉。redis叢集伺服器之間通過互相的ping-pong判斷是否節點可以連線上。如果有一半以上的節點去ping一個節點的時候沒有迴應,叢集就認為這個節點當機了。

上面就是我們常說的為了容錯而生的redis叢集投票機制

redis叢集投票機制

2.2 redis叢集(redis-cluster)的搭建

redis叢集搭建起來很簡單,我們這裡用一臺虛擬機器模擬搭建包含6個redis伺服器的叢集,實際工作中與使用多臺伺服器搭建是一個操作。

我們上面已經裝好了一個redis例項,現在我們需要把它複製6份並修改相應埠。

第一步: 新建redis-cluster資料夾

新建redis-cluster資料夾

第二步:複製redis例項

[root@Snailclimb local]# cp redis/bin redis-cluster/redis1

如果你複製過去的redis例項有dump.rdb檔案的話最好也要刪除。

一文輕鬆搞懂redis叢集原理及搭建與使用

第三步:修改配置檔案

修改bin目錄下的redis.conf配置檔案

修改配置檔案

第四步:繼續複製5個redis例項

我們用上面的redis例項複製5個redis例項,然後把他們的配置檔案的埠號改為7002-7006

繼續複製5個redis例項

第五步 :新建一個執行指令碼: [root@Snailclimb redis-cluster]# vim start-all.sh 指令碼內容如下:

cd redis1/bin
./redis-server redis.conf
cd ..
cd ..
cd redis2/bin
./redis-server redis.conf
cd ..
cd ..
cd redis3/bin
./redis-server redis.conf
cd ..
cd ..
cd redis4/bin
./redis-server redis.conf
cd ..
cd ..
cd redis5/bin
./redis-server redis.conf
cd ..
cd ..
cd redis6/bin
./redis-server redis.conf
複製程式碼

為指令碼賦予執行許可權:

[root@Snailclimb redis-cluster]# chmod u+x start-all.sh

同時啟動6個redis例項:

[root@Snailclimb redis-cluster]# ./start-all.sh

第六步:將redis-trib.rb複製到redis-cluster目錄下面:

將redis-trib.rb複製到redis-cluster目錄下面
併為指令碼賦予執行許可權: [root@Snailclimb redis-cluster]# chmod u+x redis-trib.rb

第七步:安裝ruby和ruby執行環境

yum install ruby

yum install rubygems

gem install redis-3.0.0.gem

第八步:使用ruby指令碼搭建叢集:

[root@Snailclimb redis-cluster]#./redis-trib.rb create --replicas 1 192.168.25.155:7001 192.168.25.155:7002 192.168.25.155:7003 192.168.25.155:7004 192.168.25.155:7005 192.168.25.155:7006

檢視叢集:

檢視叢集

注意:埠修改錯誤或者沒有將cluster-enabled yes前的註釋去掉都會導致叢集搭建失敗。總的來說,redis叢集搭建還是很簡單的。

這樣一個完整的redis叢集就已經搭建完畢了。。。

三 redis單機版與叢集版的測試使用

新增Maven依賴:

		<!-- Redis客戶端 -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.7.2</version>
		</dependency>
	    <dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>
複製程式碼

單機版redis測試:

	@Test
	public void testJedis() throws Exception {
		// 建立一個連線Jedis物件,引數:host、port
		Jedis jedis = new Jedis("192.168.25.155", 6379);
		// 直接使用jedis操作redis。所有jedis的命令都對應一個方法。
		jedis.set("bwcx9393", "歡迎關注Java面試通關手冊");
		String string = jedis.get("bwcx9393");
		System.out.println(string);//輸出內容:歡迎關注微信公眾號:Java面試通關手冊
		// 關閉連線
		jedis.close();
	}

複製程式碼

使用連線池測試單機版redis:

	@Test
	public void testJedisPool() throws Exception {
		// 建立一個連線池物件,兩個引數host、port
		JedisPool jedisPool = new JedisPool("192.168.25.155", 6379);
		// 從連線池獲得一個連線,就是一個jedis物件。
		Jedis jedis = jedisPool.getResource();
		// 使用jedis操作redis
		String string = jedis.get("bwcx9393");
		System.out.println(string);//輸出內容:歡迎關注微信公眾號:Java面試通關手冊
		// 關閉連線,每次使用完畢後關閉連線。連線池回收資源。
		jedis.close();
		// 關閉連線池。
		jedisPool.close();
	}

複製程式碼

測試叢集版redis:

	@Test
	public void testJedisCluster() throws Exception {
		// 建立一個JedisCluster物件。有一個引數nodes是一個set型別。set中包含若干個HostAndPort物件。
		Set<HostAndPort> nodes = new HashSet<>();
		nodes.add(new HostAndPort("192.168.25.155", 7001));
		nodes.add(new HostAndPort("192.168.25.155", 7002));
		nodes.add(new HostAndPort("192.168.25.155", 7003));
		nodes.add(new HostAndPort("192.168.25.155", 7004));
		nodes.add(new HostAndPort("192.168.25.155", 7005));
		nodes.add(new HostAndPort("192.168.25.155", 7006));
		JedisCluster jedisCluster = new JedisCluster(nodes);
		// 直接使用JedisCluster物件操作redis。
		jedisCluster.set("test", "123");
		String string = jedisCluster.get("test");
		System.out.println(string);
		// 關閉JedisCluster物件
		jedisCluster.close();
	}
複製程式碼

四 如何在JavaWeb專案中實現單機和叢集無縫切換使用

我們如何才能在專案中實現自己想用的單機redis就用單機redis想用redis叢集就用redis叢集而不要修改專案程式碼呢???

建立相應類和介面

建立相應類和介面
介面:

import java.util.List;

public interface JedisClient {

	String set(String key, String value);
	String get(String key);
	Boolean exists(String key);
	Long expire(String key, int seconds);
	Long ttl(String key);
	Long incr(String key);
	Long hset(String key, String field, String value);
	String hget(String key, String field);
	Long hdel(String key, String... field);
	Boolean hexists(String key, String field);
	List<String> hvals(String key);
	Long del(String key);
}
複製程式碼

叢集版使用:

import java.util.List;

import redis.clients.jedis.JedisCluster;

public class JedisClientCluster implements JedisClient {
	
	private JedisCluster jedisCluster;
	

	public JedisCluster getJedisCluster() {
		return jedisCluster;
	}

	public void setJedisCluster(JedisCluster jedisCluster) {
		this.jedisCluster = jedisCluster;
	}

	@Override
	public String set(String key, String value) {
		return jedisCluster.set(key, value);
	}

	@Override
	public String get(String key) {
		return jedisCluster.get(key);
	}

	@Override
	public Boolean exists(String key) {
		return jedisCluster.exists(key);
	}

	@Override
	public Long expire(String key, int seconds) {
		return jedisCluster.expire(key, seconds);
	}

	@Override
	public Long ttl(String key) {
		return jedisCluster.ttl(key);
	}

	@Override
	public Long incr(String key) {
		return jedisCluster.incr(key);
	}

	@Override
	public Long hset(String key, String field, String value) {
		return jedisCluster.hset(key, field, value);
	}

	@Override
	public String hget(String key, String field) {
		return jedisCluster.hget(key, field);
	}

	@Override
	public Long hdel(String key, String... field) {
		return jedisCluster.hdel(key, field);
	}

	@Override
	public Boolean hexists(String key, String field) {
		return jedisCluster.hexists(key, field);
	}

	@Override
	public List<String> hvals(String key) {
		return jedisCluster.hvals(key);
	}

	@Override
	public Long del(String key) {
		return jedisCluster.del(key);
	}

}
複製程式碼

單機版使用:


import java.util.List;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class JedisClientPool implements JedisClient {
	
	private JedisPool jedisPool;

	public JedisPool getJedisPool() {
		return jedisPool;
	}

	public void setJedisPool(JedisPool jedisPool) {
		this.jedisPool = jedisPool;
	}

	@Override
	public String set(String key, String value) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.set(key, value);
		jedis.close();
		return result;
	}

	@Override
	public String get(String key) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.get(key);
		jedis.close();
		return result;
	}

	@Override
	public Boolean exists(String key) {
		Jedis jedis = jedisPool.getResource();
		Boolean result = jedis.exists(key);
		jedis.close();
		return result;
	}

	@Override
	public Long expire(String key, int seconds) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.expire(key, seconds);
		jedis.close();
		return result;
	}

	@Override
	public Long ttl(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.ttl(key);
		jedis.close();
		return result;
	}

	@Override
	public Long incr(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.incr(key);
		jedis.close();
		return result;
	}

	@Override
	public Long hset(String key, String field, String value) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hset(key, field, value);
		jedis.close();
		return result;
	}

	@Override
	public String hget(String key, String field) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.hget(key, field);
		jedis.close();
		return result;
	}

	@Override
	public Long hdel(String key, String... field) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hdel(key, field);
		jedis.close();
		return result;
	}

	@Override
	public Boolean hexists(String key, String field) {
		Jedis jedis = jedisPool.getResource();
		Boolean result = jedis.hexists(key, field);
		jedis.close();
		return result;
	}

	@Override
	public List<String> hvals(String key) {
		Jedis jedis = jedisPool.getResource();
		List<String> result = jedis.hvals(key);
		jedis.close();
		return result;
	}

	@Override
	public Long del(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.del(key);
		jedis.close();
		return result;
	}

}
複製程式碼

applicationContext-redis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
	
	<!-- 連線redis單機版 -->
	<bean id="jedisClientPool" class="xx.xx.xx.JedisClientPool">
		<property name="jedisPool" ref="jedisPool"></property>
	</bean>
	<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
		<constructor-arg name="host" value="192.168.25.155"/>
		<constructor-arg name="port" value="6379"/>
	</bean>
	<!-- 連線redis叢集 -->
<!-- 	<bean id="jedisClientCluster" class="xx.xx.xx.JedisClientCluster">
		<property name="jedisCluster" ref="jedisCluster"/>
	</bean>
	<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
		<constructor-arg name="nodes">
			<set>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.155"></constructor-arg>
					<constructor-arg name="port" value="7001"></constructor-arg>
				</bean> 
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.155"></constructor-arg>
					<constructor-arg name="port" value="7002"></constructor-arg>
				</bean> 
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.155"></constructor-arg>
					<constructor-arg name="port" value="7003"></constructor-arg>
				</bean> 
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.155"></constructor-arg>
					<constructor-arg name="port" value="7004"></constructor-arg>
				</bean> 
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.155"></constructor-arg>
					<constructor-arg name="port" value="7005"></constructor-arg>
				</bean> 
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.155"></constructor-arg>
					<constructor-arg name="port" value="7006"></constructor-arg>
				</bean> 
			</set>
		</constructor-arg>
	</bean> -->
</beans>
複製程式碼

測試程式碼:

public class JedisClientTest {

	@Test
	public void testJedisClient() throws Exception {
		//初始化spring容器
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
		//從容器中獲得JedisClient物件
		JedisClient jedisClient = applicationContext.getBean(JedisClient.class);
		jedisClient.set("aaa", "111");
		String string = jedisClient.get("aaa");
		System.out.println(string);
		
		
	}
}
複製程式碼

這樣在實際專案中我們無需修改程式碼就可以實現單機和叢集版的相關切換。。

關於Redis的一些常見面試問題總結,我寫了另外一篇文章:《面試中關於Redis的問題看這篇就夠了》juejin.im/post/5ad6e4…

歡迎關注我的微信公眾號:"Java面試通關手冊"(堅持原創,分享美文,分享各種Java學習資源,面試題,以及企業級Java實戰專案回覆關鍵字免費領取):

微信公眾號

相關文章