redis的集群方案現在主要有三種(不考慮云集群),一種是豌豆莢的codis,codis是豌豆莢的團隊在redis2.8的分支上繼續(xù)開發(fā)的,基于代理的方式,生態(tài)很完整,用的人很多。還有Twitter出品的twemproxy,同樣是代理的方式,但是沒有web端的管理界面。然后就是本文要介紹的redis官方集群方案
redis-cluter作為官方出品的集群方案,15年才隨著redis3.0版本出來。當時市面上已經有了不少集群方案,所以現在的普及率不高,但是這種方式最簡單適合學習,而且我覺得一些小團隊用官方的集群方案挺好的。redis3.0集群正常工作要求至少有3個master節(jié)點,每個master節(jié)點要配置至少一個slave。redis集群一共有16384個哈希槽,數據庫中的每個key都屬于16384個中的一個,集群中每個master節(jié)點都負責一部分哈希槽
只有3個master負責讀寫,slave只是同步數據,master宕機時提升為master
- redis的官方文檔是有些問題的,它里面介紹的也是偽集群的方式,也就是一臺機器上開6個redis實例構成集群。按照官方的說明,最少配置只需要下面這些,在真正的集群方案(一臺機器一個redis實例)中是沒問題的,但是如果是同一臺機器,只配置這些,有些配置是沖突的。
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
-
介紹一下我自己的目錄結構,redis-3.2.10是源碼包,single文件夾是平時使用的一個實例的相關文件的目錄,cluster是這次新增的目錄
總目錄
cluster目錄 我的配置,和默認的redis.conf相比,需要改這些
需要說明的是:每個Redis群集節(jié)點都需要打開兩個TCP連接。用于服務客戶端的普通Redis TCP端口(例如6379),以及通過將10000添加到數據端口而獲得的端口,因此示例中為16379。
#沒什么說的,每個實例的服務端口肯定不同
port 6381
# 后臺運行,開啟后臺運行后會使用上面的pidfile文件
daemonize yes
# 每個實例要有自己的pid文件
pidfile /var/run/redis_6381.pid
# 默認是"",這樣的話當appendonly yes的時候,日志會打到/dev/null中
# 也就是日志被吞了,還是打印日志讓人放心
logfile "/usr/local/redis/cluster/6381/redis6381.log"
# 默認16個庫,沒什么意義,redis-cluster不支持換庫
databases 1
# rdb文件名
dbfilename dump6381.rdb
# 存放rdb和aof的文件,默認是./
# 也就是在哪啟動實例,就把rdb和aof文件放在哪,這太惡心了
dir /usr/local/redis/cluster/6381
# 開啟aof,集群的話必須開啟
appendonly yes
# aof文件名
appendfilename "appendonly6381.aof"
# 默認是被注釋掉的
cluster-enabled yes
# 默認是被注釋掉的,這個文件不需要自己操作,redis自動操作這個文件
cluster-config-file nodes-6381.conf
# 默認是被注釋掉的
cluster-node-timeout 15000
- 然后把6個實例都啟動,執(zhí)行redis-3.2.10/src/redis-trib.rb這個redis自帶的ruby腳本。--replicas 1 代表每個master一個slave
sudo redis-trib.rb create --replicas 1 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 127.0.0.1:6386 - 啟動后會給出一個方案,哪三個是master,哪三個是slave,接受的話輸入yes集群就創(chuàng)建成功了
這里有坑,mac自帶了ruby2.0.0可以運行這個腳本,但是這個腳本需要redis.gem才能讓ruby使用redis的接口,當運行gem install redis的時候會安裝最新的4.0.1版本,該版本要求ruby2.2.2以上。這里有兩個解決方案,一是升級mac自帶的ruby版本,推薦使用rym升級ruby。二是使用低版本的redis.gem。
我選擇的第二種方式,在官網看到只有4版本對ruby版本有要求,所以使用3.3.5這個3的最后一個版本。執(zhí)行:
sudo gem install redis -v 3.3.5
- redis-3.2.10/utils/create-cluster/create-cluster的這個腳本可以用來參考,寫一個自己的啟動、關閉集群的腳本
- redis-trib.rb這個腳本只負責創(chuàng)建維護集群,不負責啟動和關閉集群的redis實例,所有每次創(chuàng)建集群都需要開啟6個實例,每次關閉集群都要關閉6個實例,很麻煩
Tips
redis的官方集群方案,用起來比其他的方式簡單很多很多,很是和自己弄著玩
- redis-cluster在spring中的配置
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!--新版是maxTotal,舊版是maxActive,最大連接數-->
<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報異常,true阻塞直到超時,默認true-->
<property name="blockWhenExhausted" value="false"/>
</bean>
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster" destroy-method="close">
<constructor-arg name="jedisClusterNode">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6381"/>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6382"/>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6383"/>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6384"/>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6385"/>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6386"/>
</bean>
</set>
</constructor-arg>
<constructor-arg name="timeout" value="2000"/>
<constructor-arg name="maxAttempts" value="100"/>
<constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
</bean>
- redis3版本之前可以用jedis提供的分片客戶端功能,spring配置
<bean id="sharedJedisPool" class="redis.clients.jedis.ShardedJedisPool" destroy-method="close">
<constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
<constructor-arg name="shards">
<list>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6379"/>
</bean>
</list>
</constructor-arg>
</bean>
ShardedJedisPool作為jedis在redis3之前的分片客戶端的實現,相當于根據key的不同,將數據存入不同的redis實例,所以每個redis實例中數據是不同的。JedisCluster作為jedis在redis3之后的集群實現,客戶端不會根據key來分片,redis-cluster實現這個功能,并且集群中的每個redis都可以拿到整個集群的全部數據,雖然redis實例間數據還是不同,但會通過轉發(fā)拿到別的redis實例的數據
- 平時自己開發(fā)用,可以使用單實例redis的配置
<bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
<constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
<constructor-arg name="host" value="localhost"/>
<constructor-arg name="port" value="6379"/>
</bean>

