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

今天早上因爲zookeeper和redis集羣不在同一虛擬機致使出了點很小錯誤(人爲),因此這裏總結一下redis集羣的搭建以便往後所需同時也但願能對你有所幫助。 筆主這裏使用的是Centos7.若是你碰到任何問題均可以來問我,留言或者加我微信:bwcx9393.php

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

一 redis的安裝

Redis是c語言開發的。 安裝redis須要c語言的編譯環境。若是沒有gcc須要在線安裝:yum install gcc-c++java

第一步:獲取源碼包:wget http://download.redis.io/releases/redis-3.0.0.tar.gznode

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

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

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

PREFIX參數指定redis的安裝目錄。通常軟件安裝到/usr目錄下算法

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

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

修改配置文件:把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文件的話最好也要刪除。

第三步:修改配置文件

修改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實戰項目回覆關鍵字免費領取):

微信公衆號
相關文章
相關標籤/搜索