您當前位置: 南順網絡>> 官方資訊>> 建站知識

redis分布式搭建

Redis集群架構圖

 

上圖藍色為(wèi)redis集群的(de)節點。


節點之間通過ping命令來測試連接是否正常,節點之間沒有主區分,連接到任何一(yī)個節點進行操作時,都可(kě)能會轉發到其他節點。


1、Redis的(de)容錯機制

節點之間會定時的(de)互相發送ping命令,測試節點的(de)健康狀态,當節點接受到ping命令後,會返回一(yī)個pong字符串。


投票(piào)機制:如(rú)果一(yī)個節點A給節點B發送ping沒有得到pong返回,會通知其他節點再次給B發送ping,如(rú)果集群中有超過一(yī)半的(de)節點收不B節點的(de)pong。那麽就認為(wèi)B節點挂了。一(yī)般會為(wèi)每個節點提供一(yī)個備份節點,如(rú)果挂掉會切換到備份節點。


2、Redis集群存儲原理(lǐ)

Redis對于每個存放的(de)key會進行hash操作,生成一(yī)個[0-16384]的(de)hash值(先進行


crc 算法再對16384取餘)。


集群的(de)情況下,就是把[0-16384]的(de)區間進行拆分,放到不同的(de)redis中。


 


3、Redis的(de)持久化

Snapshotting:定時的(de)将Redis內(nèi)存中的(de)數據保存到硬盤中


AOF:将所有的(de)command操作保存到aof中,AOP的(de)同步頻率很高(gāo),數據即使丢失,粒度也很小,但會在性能上造成影響。


 


二、Redis集群準備工作

Redis安裝

源碼下載


下載地(dì)址https://pan.baidu.com/s/1bCcLv4  密碼i5k6


解壓源碼


   tar -zxvf redis-3.0.0.tar.gz  


進入解壓後的(de)目錄進行編譯


cd /usr/local/redis-3.0.0


make


安裝到指定目錄,如(rú) /usr/local/redis


cd /usr/local/redis-3.0.0


make PREFIX=/usr/local/redis install


nredis.conf


redis.conf是redis的(de)配置文件,redis.conf在redis源碼目錄。


注意修改port作為(wèi)redis進程的(de)端口,port默認6379。


 


拷貝配置文件到安裝目錄下


進入源碼目錄,裏面有一(yī)份配置文件 redis.conf,然後将其拷貝到安裝路徑下


cd /usr/local/redis


mkdir conf


cp /usr/local/redis-3.0.0/redis.conf  /usr/local/redis/bin


運行:bin/redis-server  将出現下圖畫面:


 


 


Redis默認是前台運行的(de),可(kě)以修改redis.conf的(de)daemonize yes ,将其變成後台運行。


 


集群環境搭建

redis集群管理(lǐ)工具redis-trib.rb依賴ruby環境,首先需要安裝ruby環境


安裝ruby


yum install ruby


yum install rubygems


安裝ruby和(hé)redis的(de)接口程序


拷貝redis-3.0.0.gem至/usr/local下


執行:


gem install /usr/local/redis-3.0.0.gem


 


三、創建Redis集群

在一(yī)台服務器上,可(kě)以用不同端口号來表示不同redis服務器。


Redis集群最少需要三台服務器,而每台服務器有需要備用服務器,所以最少需要6台服務器。端口規劃如(rú)下:


主服務器:192.168.100.66 :7001  :7002  :7003


從服務器:192.168.100.66 :7004  :7005  :7006


在/usr/local 創建文件夾用來存放服務器程序


mkdir 7001 7002 7003 7004 7005 7006


如(rú)果想讓redis支持集群需要修改redis.config配置文件的(de)cluster-enabled yes


本例中我們以端口來區别不同的(de)redis服務,所以還需要修改redis.config的(de)port為(wèi)對應端口


修改完配置文件,将redis安裝目錄的(de)bin複制到上面每個目錄中。


分别進入7001/bin/ 7002/bin .....


啓動服務./redis-server ./redis.conf



查看redis進程:ps -aux|grep redis 如(rú)下圖則說明啓動成功


 


創建集群:


将之前解壓的(de)文件夾的(de)redis-3.0.0/src/redis-trib.rb複制到redis-cluster目錄


運行


./redis-trib.rb create --replicas 1 192.168.100.66:7001 192.168.100.66:7002 192.168.100.66:7003 192.168.100.66:7004 192.168.100.66:7005  192.168.100.66:7006


 


如(rú)果執行時報如(rú)下錯誤:


[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0


解決方法是删除生成的(de)配置文件nodes.conf,如(rú)果不行則說明現在創建的(de)結點包括了舊(jiù)集群的(de)結點信息,需要删除redis的(de)持久化文件後再重啓redis,比如(rú):appendonly.aof、dump.rdb


如(rú)果成功最終輸入如(rú)下:

 


查詢集群信息:



說明:


./redis-cli -c -h 192.168.101.3 -p 7001 ,其中-c表示以集群方式連接redis,-h指定ip地(dì)址,-p指定端口号


cluster nodes 查詢集群結點信息


cluster info 查詢集群狀态信息

 


hash槽重新分配

第一(yī)步:連接上集群


./redis-trib.rb reshard 192.168.101.3:7001(連接集群中任意一(yī)個可(kě)用結點都行)


 


第二步:輸入要分配的(de)槽數量

 


輸入 500表示要分配500個槽


 


第三步:輸入接收槽的(de)結點id

 


 


這裏準備給7007分配槽,通過cluster nodes查看7007結點id為(wèi)15b809eadae88955e36bcdbb8144f61bbbaf38fb


輸入:15b809eadae88955e36bcdbb8144f61bbbaf38fb


 


第四步:輸入源結點id

 


這裏輸入all


第五步:輸入yes開始移動槽到目标結點id

 

添加從節點

 


集群創建成功後可(kě)以向集群中添加節點,下面是添加一(yī)個slave從節點。


添加7008從結點,将7008作為(wèi)7007的(de)從結點。


 


./redis-trib.rb add-node --slave --master-id 主節點id 添加節點的(de)ip和(hé)端口 集群中已存在節點ip和(hé)端口

 


 


執行如(rú)下命令:


./redis-trib.rb add-node --slave --master-id cad9f7413ec6842c971dbcc2c48b4ca959eb5db4  192.168.101.3:7008 192.168.101.3:7001


cad9f7413ec6842c971dbcc2c48b4ca959eb5db4  是7007結點的(de)id,可(kě)通過cluster nodes查看。


 


注意:如(rú)果原來該結點在集群中的(de)配置信息已經生成cluster-config-file指定的(de)配置文件中(如(rú)果cluster-config-file沒有指定則默認為(wèi)nodes.conf),這時可(kě)能會報錯:


[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0


解決方法是删除生成的(de)配置文件nodes.conf,删除後再執行./redis-trib.rb add-node指令


查看集群中的(de)結點,剛添加的(de)7008為(wèi)7007的(de)從節點:

 


1.1. 删除結點:


./redis-trib.rb del-node 127.0.0.1:7005 4b45eb75c8b428fbd77ab979b85080146a9bc017


删除已經占有hash槽的(de)結點會失敗,報錯如(rú)下:


[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.


需要将該結點占用的(de)hash槽分配出去(qù)(參考hash槽重新分配章(zhāng)節)。


測試:


Maven:


<dependencies>

    <dependency>

        <groupId>redis.clients</groupId>

        <artifactId>jedis</artifactId>

        <version>2.7.0</version>

    </dependency>

    <!-- https://mvnrepository.com/artifact/junit/junit -->

    <dependency>

        <groupId>junit</groupId>

        <artifactId>junit</artifactId>

        <version>4.12</version>

        <scope>test</scope>

    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->

    <dependency>

        <groupId>org.springframework</groupId>

        <artifactId>spring-test</artifactId>

        <version>4.3.10.RELEASE</version>

        <scope>test</scope>

    </dependency>

</dependencies>


 


 


普通測試:


@Test

public void redisClusterTest1(){

    JedisPoolConfig config=new JedisPoolConfig();

    config.setMaxTotal(30);

    config.setMaxIdle(2);


    Set<HostAndPort> jedisNode=new HashSet<HostAndPort>();

    jedisNode.add(new HostAndPort("192.168.100.66",7001));

    jedisNode.add(new HostAndPort("192.168.100.66",7002));

    jedisNode.add(new HostAndPort("192.168.100.66",7003));

    jedisNode.add(new HostAndPort("192.168.100.66",7004));

    jedisNode.add(new HostAndPort("192.168.100.66",7005));

    jedisNode.add(new HostAndPort("192.168.100.66",7006));


    JedisCluster jc=new JedisCluster(jedisNode,config);

    jc.set("name","老王");

    String value=jc.get("name");

    System.out.println(value);

}


Spring測試:


配置文件:


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       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.xsd">

    <!-- 連接池配置 -->

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">

        <!-- 最大連接數 -->

        <property name="maxTotal" value="30" />

        <!-- 最大空閑連接數 -->

        <property name="maxIdle" value="10" />

        <!-- 每次釋放連接的(de)最大數目 -->

        <property name="numTestsPerEvictionRun" value="1024" />

        <!-- 釋放連接的(de)掃描間隔(毫秒) -->

        <property name="timeBetweenEvictionRunsMillis" value="30000" />

        <!-- 連接最小空閑時間 -->

        <property name="minEvictableIdleTimeMillis" value="1800000" />

        <!-- 連接空閑多久後釋放, 當空閑時間>該值 且 空閑連接>最大空閑連接數 時直接釋放 -->

        <property name="softMinEvictableIdleTimeMillis" value="10000" />

        <!-- 獲取連接時的(de)最大等待毫秒數,小于零:阻塞不确定的(de)時間,默認-1 -->

        <property name="maxWaitMillis" value="1500" />

        <!-- 在獲取連接的(de)時候檢查有效性, 默認false -->

        <property name="testOnBorrow" value="true" />

        <!-- 在空閑時檢查有效性, 默認false -->

        <property name="testWhileIdle" value="true" />

        <!-- 連接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true -->

        <property name="blockWhenExhausted" value="false" />

    </bean>

    <!-- redis集群 -->

    <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">

        <constructor-arg index="0">

            <set>

                <bean class="redis.clients.jedis.HostAndPort">

                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>

                    <constructor-arg index="1" value="7001"></constructor-arg>

                </bean>

                <bean class="redis.clients.jedis.HostAndPort">

                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>

                    <constructor-arg index="1" value="7002"></constructor-arg>

                </bean>

                <bean class="redis.clients.jedis.HostAndPort">

                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>

                    <constructor-arg index="1" value="7003"></constructor-arg>

                </bean>

                <bean class="redis.clients.jedis.HostAndPort">

                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>

                    <constructor-arg index="1" value="7004"></constructor-arg>

                </bean>

                <bean class="redis.clients.jedis.HostAndPort">

                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>

                    <constructor-arg index="1" value="7005"></constructor-arg>

                </bean>

                <bean class="redis.clients.jedis.HostAndPort">

                    <constructor-arg index="0" value="192.168.100.66"></constructor-arg>

                    <constructor-arg index="1" value="7006"></constructor-arg>

                </bean>

            </set>

        </constructor-arg>

        <constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>

    </bean>

</beans>


 


測試類:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration({"classpath:spring-config.xml"})

public class RedisClusterTest {

    @Autowired

    private JedisCluster jedisCluster;

    @Test

    public void redisClusterTest2(){

        jedisCluster.set("username","小明啦啦");

        String name=jedisCluster.get("username");

        System.out.println(name);

    }

}


編輯:--ns868