Redis偽叢集的搭建
阿新 • • 發佈:2018-12-30
Redis介紹
1.什麼是Redis?
Redis是用C語言開發的一個開源的高效能鍵值對(key-value)資料庫。它通過提供多種鍵值資料型別來適應不同場景下的儲存需求,目前為止Redis支援的鍵值資料型別如下:
- 字串型別
- 雜湊型別
- 列表型別
- 集合型別
2.redis的應用場景
- 快取(資料查詢、短連線、新聞內容、商品內容等等)。(最多使用)
- 分散式叢集架構中的session分離。
- 聊天室的線上好友列表。
- 任務佇列。(秒殺、搶購、12306等等)
- 應用排行榜。
- 網站訪問統計。
- 資料過期處理(可以精確到毫秒)
3.Redis單機版的安裝
redis是C語言開發,建議在linux上執行,本文使用Centos6.4作為安裝環境。
安裝redis需要先將官網下載的原始碼進行編譯,編譯依賴gcc環境,如果沒有gcc環境,需要安裝gcc:yum install gcc-c++
- 版本說明
本教程使用redis3.0版本。3.0版本主要增加了redis叢集功能。 - 解壓原始碼
tar -zxvf redis-3.0.0.tar.gz - 進入解壓後的目錄進行編譯
cd /usr/local/redis-3.0.0
make - 安裝到指定目錄,如 /usr/local/redis
cd /usr/local/redis-3.0.0
make PREFIX=/usr/local/redis install - redis.conf
redis.conf是redis的配置檔案,redis.conf在redis原始碼目錄。
注意修改port作為redis程序的埠,port預設6379。 - 拷貝配置檔案到安裝目錄下
進入原始碼目錄,裡面有一份配置檔案 redis.conf,然後將其拷貝到安裝路徑下
cd /usr/local/redis
mkdir conf
cp /usr/local/redis-3.0.0/redis.conf /usr/local/redis/bin - 安裝目錄bin下的檔案列表
redis3.0新增的redis-sentinel是redis叢集管理工具可實現高可用。
配置檔案目錄:
4.redis啟動
- 前端模式啟動
直接執行bin/redis-server將以前端模式啟動,前端模式啟動的缺點是ssh命令視窗關閉則redis-server程式結束,不推薦使用此方法。如下圖:
- 後端模式啟動
修改redis.conf配置檔案, daemonize yes 以後端模式啟動。
執行如下命令啟動redis:
cd /usr/local/redis
./bin/redis-server ./redis.conf
redis預設使用6379埠。
也可更改redis.conf檔案,修改埠號:
5.redis叢集
- redis-cluster架構圖
架構細節:
(1)所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位制協議優化傳輸速度和頻寬。
(2)節點的fail是通過叢集中超過半數的節點檢測失效時才生效。
(3)客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連線叢集所有節點,連線叢集中任何一個可用節點即可。
(4)redis-cluster把所有的物理節點對映到[0-16383]slot上,cluster 負責維護node<->slot<->value。
(5)Redis 叢集中內建了 16384 個雜湊槽,當需要在 Redis 叢集中放置一個 key-value 時,redis 先對 key 使用 crc16 演算法算出一個結果,然後把結果對 16384 求餘數,這樣每個 key 都會對應一個編號在 0-16383 之間的雜湊槽,redis 會根據節點數量大致均等的將雜湊槽對映到不同的節點。 - redis-cluster投票:容錯
(1)領著投票過程是叢集中所有master參與,如果半數以上master節點與master節點通訊超過(cluster-node-timeout),認為當前master節點掛掉。
(2):什麼時候整個叢集不可用(cluster_state:fail)?
a:如果叢集任意master掛掉,且當前master沒有slave.叢集進入fail狀態,也可以理解成叢集的slot對映[0-16383]不完成時進入fail狀態. ps : redis-3.0.0.rc1加入cluster-require-full-coverage引數,預設關閉,開啟叢集相容部分失敗。
b:如果叢集超過半數以上master掛掉,無論是否有slave叢集進入fail狀態。
ps:當叢集不可用時,所有對叢集的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)錯誤。 - ruby環境
redis叢集管理工具redis-trib.rb依賴ruby環境,首先需要安裝ruby環境:
安裝ruby
yum install ruby
yum install rubygems
安裝ruby和redis的介面程式
拷貝redis-3.0.0.gem至/usr/local下
redis-3.0.0.gem下載地址:
http://download.csdn.net/detail/nayouzenmyang/9731848
執行:
gem install /usr/local/redis-3.0.0.gem
6.建立叢集
- 叢集結點規劃
這裡在同一臺伺服器用不同的埠表示不同的redis伺服器,如下:
主節點:192.168.101.3:7001 192.168.101.3:7002 192.168.101.3:7003
從節點:192.168.101.3:7004 192.168.101.3:7005 192.168.101.3:7006
在/usr/local下建立redis-cluster目錄,其下建立7001、7002。。7006目錄,如下:
將redis安裝目錄bin下的檔案拷貝到每個700X目錄內,同時將redis原始碼目錄src下的redis-trib.rb拷貝到redis-cluster目錄下。
修改每個700X目錄下的redis.conf配置檔案:
port XXXX
#bind 192.168.101.3
cluster-enabled yes - 啟動每個結點redis服務
分別進入7001、7002、…7006目錄,執行:
./redis-server ./redis.conf
檢視redis程序:
- 執行建立叢集命令
執行redis-trib.rb,此指令碼是ruby指令碼,它依賴ruby環境。
./redis-trib.rb create –replicas 1 192.168.101.3:7001 192.168.101.3:7002 192.168.101.3:7003 192.168.101.3:7004 192.168.101.3:7005 192.168.101.3:7006
說明:
redis叢集至少需要3個主節點,每個主節點有一個從節點總共6個節點
replicas指定為1表示每個主節點有一個從節點
注意:
如果執行時報如下錯誤:
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
解決方法是刪除生成的配置檔案nodes.conf,如果不行則說明現在建立的結點包括了舊叢集的結點資訊,需要刪除redis的持久化檔案後再重啟redis,比如:appendonly.aof、dump.rdb
建立叢集輸出如下:
>>> Creating cluster
Connecting to node 192.168.101.3:7001: OK
Connecting to node 192.168.101.3:7002: OK
Connecting to node 192.168.101.3:7003: OK
Connecting to node 192.168.101.3:7004: OK
Connecting to node 192.168.101.3:7005: OK
Connecting to node 192.168.101.3:7006: OK
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.101.3:7001
192.168.101.3:7002
192.168.101.3:7003
Adding replica 192.168.101.3:7004 to 192.168.101.3:7001
Adding replica 192.168.101.3:7005 to 192.168.101.3:7002
Adding replica 192.168.101.3:7006 to 192.168.101.3:7003
M: cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 192.168.101.3:7001
slots:0-5460 (5461 slots) master
M: 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841 192.168.101.3:7002
slots:5461-10922 (5462 slots) master
M: 1a8420896c3ff60b70c716e8480de8e50749ee65 192.168.101.3:7003
slots:10923-16383 (5461 slots) master
S: 69d94b4963fd94f315fba2b9f12fae1278184fe8 192.168.101.3:7004
replicates cad9f7413ec6842c971dbcc2c48b4ca959eb5db4
S: d2421a820cc23e17a01b597866fd0f750b698ac5 192.168.101.3:7005
replicates 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841
S: 444e7bedbdfa40714ee55cd3086b8f0d5511fe54 192.168.101.3:7006
replicates 1a8420896c3ff60b70c716e8480de8e50749ee65
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join...
>>> Performing Cluster Check (using node 192.168.101.3:7001)
M: cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 192.168.101.3:7001
slots:0-5460 (5461 slots) master
M: 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841 192.168.101.3:7002
slots:5461-10922 (5462 slots) master
M: 1a8420896c3ff60b70c716e8480de8e50749ee65 192.168.101.3:7003
slots:10923-16383 (5461 slots) master
M: 69d94b4963fd94f315fba2b9f12fae1278184fe8 192.168.101.3:7004
slots: (0 slots) master
replicates cad9f7413ec6842c971dbcc2c48b4ca959eb5db4
M: d2421a820cc23e17a01b597866fd0f750b698ac5 192.168.101.3:7005
slots: (0 slots) master
replicates 4e7c2b02f0c4f4cfe306d6ad13e0cfee90bf5841
M: 444e7bedbdfa40714ee55cd3086b8f0d5511fe54 192.168.101.3:7006
slots: (0 slots) master
replicates 1a8420896c3ff60b70c716e8480de8e50749ee65
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
- 查詢叢集資訊
叢集建立成功登陸任意redis結點查詢叢集中的節點情況。
客戶端以叢集方式登陸:
說明:
./redis-cli -c -h 192.168.101.3 -p 7001 ,其中-c表示以叢集方式連線redis,-h指定ip地址,-p指定埠號
cluster nodes 查詢叢集結點資訊
cluster info 查詢叢集狀態資訊
- 新增主節點
叢集建立成功後可以向叢集中新增節點,下面是新增一個master主節點
新增7007結點,參考叢集結點規劃章節新增一個“7007”目錄作為新節點。
執行下邊命令:
./redis-trib.rb add-node 192.168.101.3:7007 192.168.101.3:7001
檢視叢集結點發現7007已新增到叢集中:
- hash槽重新分配
新增完主節點需要對主節點進行hash槽分配這樣該主節才可以儲存資料。
redis叢集有16384個槽,叢集中的每個結點分配自已槽,通過檢視叢集結點可以看到槽佔用情況。
給剛新增的7007結點分配槽:
第一步:連線上叢集
./redis-trib.rb reshard 192.168.101.3:7001(連線叢集中任意一個可用結點都行)
第二步:輸入要分配的槽數量
輸入 500表示要分配500個槽
第三步:輸入接收槽的結點id
這裡準備給7007分配槽,通過cluster nodes檢視7007結點id為15b809eadae88955e36bcdbb8144f61bbbaf38fb
輸入:15b809eadae88955e36bcdbb8144f61bbbaf38fb
第四步:輸入源結點id
這裡輸入all
第五步:輸入yes開始移動槽到目標結點id
- 新增從節點
叢集建立成功後可以向叢集中新增節點,下面是新增一個slave從節點。
新增7008從結點,將7008作為7007的從結點。
./redis-trib.rb add-node –slave –master-id 主節點id 新增節點的ip和埠 叢集中已存在節點ip和埠
執行如下命令:
./redis-trib.rb add-node –slave –master-id cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 192.168.101.3:7008 192.168.101.3:7001
cad9f7413ec6842c971dbcc2c48b4ca959eb5db4 是7007結點的id,可通過cluster nodes檢視。
注意:如果原來該結點在叢集中的配置資訊已經生成cluster-config-file指定的配置檔案中(如果cluster-config-file沒有指定則預設為nodes.conf),這時可能會報錯:
[ERR] Node XXXXXX is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
解決方法是刪除生成的配置檔案nodes.conf,刪除後再執行./redis-trib.rb add-node指令
檢視叢集中的結點,剛新增的7008為7007的從節點:
- 刪除結點
./redis-trib.rb del-node 127.0.0.1:7005 4b45eb75c8b428fbd77ab979b85080146a9bc017
刪除已經佔有hash槽的結點會失敗,報錯如下:
[ERR] Node 127.0.0.1:7005 is not empty! Reshard data away and try again.
需要將該結點佔用的hash槽分配出去(參考hash槽重新分配)。
7.jedisCluster
-測試程式碼
// 連線redis叢集
@Test
public void testJedisCluster() {
JedisPoolConfig config = new JedisPoolConfig();
// 最大連線數
config.setMaxTotal(30);
// 最大連線空閒數
config.setMaxIdle(2);
//叢集結點
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7001));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7002));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7003));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7004));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7005));
jedisClusterNode.add(new HostAndPort("192.168.101.3", 7006));
JedisCluster jc = new JedisCluster(jedisClusterNode, config);
JedisCluster jcd = new JedisCluster(jedisClusterNode);
jcd.set("name", "zhangsan");
String value = jcd.get("name");
System.out.println(value);
}
- 使用spring
配置applicationContext.xml
<!-- 連線池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大連線數 -->
<property name="maxTotal" value="30" />
<!-- 最大空閒連線數 -->
<property name="maxIdle" value="10" />
<!-- 每次釋放連線的最大數目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 釋放連線的掃描間隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 連線最小空閒時間 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 連線空閒多久後釋放, 當空閒時間>該值 且 空閒連線>最大空閒連線數 時直接釋放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 獲取連線時的最大等待毫秒數,小於零:阻塞不確定的時間,預設-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在獲取連線的時候檢查有效性, 預設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.101.3"></constructor-arg>
<constructor-arg index="1" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.101.3"></constructor-arg>
<constructor-arg index="1" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.101.3"></constructor-arg>
<constructor-arg index="1" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.101.3"></constructor-arg>
<constructor-arg index="1" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.101.3"></constructor-arg>
<constructor-arg index="1" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort"> <constructor-arg index="0" value="192.168.101.3"></constructor-arg>
<constructor-arg index="1" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
<constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
</bean>
測試程式碼
private ApplicationContext applicationContext;
@Before
public void init() {
applicationContext = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");
}
//redis叢集
@Test
public void testJedisCluster() {
JedisCluster jedisCluster = (JedisCluster) applicationContext.getBean("jedisCluster");
jedisCluster.set("name", "zhangsan");
String value = jedisCluster.get("name");
System.out.println(value);
}