Redis集群模式与代理模式
为了解决redis主从复制或者哨兵模式一个实例存储全量的数据,内存空间可能不足的问题,需要对数据进行分区(Partitioning)。
分区的实现方式
- 客户端分区:例如原来一个商城的所有缓存都存在一个redis,现在按模块进行区分,订单模块放到一个redis,支付模块放到一个redis。
- 代理分区:客户端将请求发送给代理端,代理根据一致性哈希算法算出key对应在哪个redis实例上,然后将请求发送给对应的redis实例,最后将结果返回给客户端。
- 查询路由:将请求随机发给一个redis实例,然后这个实例会将请求转发给正确的redis实例,集群模式就是基于这个原理。
集群模式
集群模式会自动对数据进行分区,存储到不同的节点上,集群通过分区来提供一定程度的可用性,当某个节点宕机或者不可达的情况下继续处理请求。
那么集群模式怎么对数据进行分片呢?使用哈希槽。
Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:
- 节点A包含0到5500号哈希槽。
- 节点B包含5501到11000号哈希槽。
- 节点C包含11001到16384号哈希槽。
如果要添加节点D,可以从节点A, B, C中分出部分槽到D上,如果要移除节点A,可以将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可。由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
另外集群模式中还使用了主从复制模型,每个master节点都要有1个或者多个slave,也就是上面具有A,B,C三个节点的集群中的每个节点还需要添加一个从节点,这样master节点挂了后,slave就会成为新的master继续提供服务,整个集群便不会因为key找不到对应的槽而不可用了。
快速搭建集群环境,create-cluster位于redis源码目录/root/redis-6.0.6/utils/create-cluster:
# ./create-cluster start
Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006
# ./create-cluster create
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:30005 to 127.0.0.1:30001
Adding replica 127.0.0.1:30006 to 127.0.0.1:30002
Adding replica 127.0.0.1:30004 to 127.0.0.1:30003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 0107a13e16bdf51da044f49fa5613ac63ec40bbe 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
M: ef25f43ffd11573c3bd173e81163aa08754b895f 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
M: f0f906cf8a87dd8bafc3a8debcdcdf1af707d8c5 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
S: dac8c0c91741d3068ce057831945cd6d16b8becc 127.0.0.1:30004
replicates 0107a13e16bdf51da044f49fa5613ac63ec40bbe
S: 885736999bfbef09f5fcaa1dc7639cc360b19d81 127.0.0.1:30005
replicates ef25f43ffd11573c3bd173e81163aa08754b895f
S: 4bd69bbac358d9aa988caf2d9e0fc84395a08b6e 127.0.0.1:30006
replicates f0f906cf8a87dd8bafc3a8debcdcdf1af707d8c5
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 127.0.0.1:30001)
M: 0107a13e16bdf51da044f49fa5613ac63ec40bbe 127.0.0.1:30001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: 885736999bfbef09f5fcaa1dc7639cc360b19d81 127.0.0.1:30005
slots: (0 slots) slave
replicates ef25f43ffd11573c3bd173e81163aa08754b895f
S: dac8c0c91741d3068ce057831945cd6d16b8becc 127.0.0.1:30004
slots: (0 slots) slave
replicates 0107a13e16bdf51da044f49fa5613ac63ec40bbe
S: 4bd69bbac358d9aa988caf2d9e0fc84395a08b6e 127.0.0.1:30006
slots: (0 slots) slave
replicates f0f906cf8a87dd8bafc3a8debcdcdf1af707d8c5
M: ef25f43ffd11573c3bd173e81163aa08754b895f 127.0.0.1:30002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: f0f906cf8a87dd8bafc3a8debcdcdf1af707d8c5 127.0.0.1:30003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
就这样,集群模式搭建好了,大体架构如下:
使用redis-cli -c -p 30001
对30001节点进行连接,注意要带上-c
参数,不然set时会报错(error) MOVED 5798 127.0.0.1:30002
:
# redis-cli -c -p 30001
127.0.0.1:30001> set name morris
-> Redirected to slot [5798] located at 127.0.0.1:30002
OK
由于集群模式中对数据进行了分片,对key进行聚合操作以及事务等都很难实现,如果想要某些key落在同一个redis实例中,该怎么实现呢?键哈希标签。
键哈希标签(Keys hash tags):可以确保带有相同标签的键都会在同一个哈希槽。
127.0.0.1:30001> set {xxx}name morris
OK
127.0.0.1:30001> set {xxx}age 18
OK
127.0.0.1:30001> keys *
1) "{xxx}age"
2) "{xxx}name"
将快速搭建的集群关闭:
# ./create-cluster stop 关闭redis
# ./create-cluster clean 清理数据
下面使用命令来搭建集群,有关集群使用的命令如下:
# redis-cli --cluster help
Cluster Manager Commands:
create host1:port1 ... hostN:portN
--cluster-replicas <arg>
check host:port
--cluster-search-multiple-owners
info host:port
fix host:port
--cluster-search-multiple-owners
--cluster-fix-with-unreachable-masters
reshard host:port
--cluster-from <arg>
--cluster-to <arg>
--cluster-slots <arg>
--cluster-yes
--cluster-timeout <arg>
--cluster-pipeline <arg>
--cluster-replace
rebalance host:port
--cluster-weight <node1=w1...nodeN=wN>
--cluster-use-empty-masters
--cluster-timeout <arg>
--cluster-simulate
--cluster-pipeline <arg>
--cluster-threshold <arg>
--cluster-replace
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id <arg>
del-node host:port node_id
call host:port command arg arg .. arg
set-timeout host:port milliseconds
import host:port
--cluster-from <arg>
--cluster-copy
--cluster-replace
backup host:port backup_directory
help
For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
先借助之前的脚本,启动6个redis实例:
# ./create-cluster start
创建集群:
# redis-cli --cluster create 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas 1
如果由配置文件启动的redis,需要开启如下配置,否则无法使用集群:
cluster-enabled yes
代理模式
目前比较流行的代理框架:
- predixy:高性能全特征redis代理,支持Redis Sentinel和Redis Cluster。
- twemproxy:快速、轻量级memcached和redis代理,Twitter推特公司开发。
- codis:redis集群代理解决方案,豌豆荚公司开发,需要修改redis源码。
predixy
github地址:https://github.com/joyieldInc/predixy
直接下载已经编译好的二进制文件,下载地址:https://github.com/joyieldInc/predixy/releases/download/1.0.5/predixy-1.0.5-bin-amd64-linux.tar.gz
按照如下架构进行部署:
修改配置文件predixy-1.0.5/conf/predixy.conf
Bind 127.0.0.1:7617 # 开启
Include sentinel.conf # 开启
# Include try.conf # 注释掉
在predixy-1.0.5/conf/sentinel.conf
中增加如下配置:
SentinelServerPool {
Databases 16
Hash crc16
HashTag "{}"
Distribution modula
MasterReadPriority 60
StaticSlaveReadPriority 50
DynamicSlaveReadPriority 50
RefreshInterval 1
ServerTimeout 1
ServerFailureLimit 10
ServerRetryTimeout 1
KeepAlive 120
Sentinels {
+ 127.0.0.1:26379
+ 127.0.0.1:26380
+ 127.0.0.1:26381
}
Group ooxx {
}
Group xxoo {
}
}
找一个目录用来存放redis的数据:
# mkdir 26379
# mkdir 26380
# mkdir 26381
# mkdir 36379
# mkdir 36380
# mkdir 46379
# mkdir 46380
在26379、26380、26381目录下新建sentinel.conf文件,内容如下:
port 26379
sentinel monitor xxoo 127.0.0.1 36379 2
启动三台哨兵:
# redis-server 26379/sentinel.conf --sentinel
# redis-server 26380/sentinel.conf --sentinel
# redis-server 26381/sentinel.conf --sentinel
启动两主两从:
# redis-server --dir 36379 --port 36379
# redis-server --dir 36380 --port 36380 --replicaof 127.0.0.1 36379
# redis-server --dir 46379 --port 46379
# redis-server --dir 46380 --port 46380 --replicaof 127.0.0.1 46379
启动predixy,并使用:
# sh bin/predixy conf/predixy.conf
# redis-cli -p 7617
127.0.0.1:7617> set name morris
OK
127.0.0.1:7617> set age 18
OK
twemproxy
github地址:https://github.com/twitter/twemproxy
安装twemproxy之前先安装automake和libtool:
# yum install -y automake libtool
# git clone git@github.com:twitter/twemproxy.git
# cd twemproxy
# autoreconf -fvi
# ./configure
# make
编译完后会在twemproxy/src目录下生成nutcracker命令,我们把他加入到系统环境中。
# cp src/nutcracker /usr/bin/
将nutcracker安装成为服务,并配置随开机启动:
# cp scripts/nutcracker.init /etc/init.d/nutcracker
# chkconfig --add nutcracker
新建目录/etc/nutcracker,并把配置文件复制到里面:
# mkdir /etc/nutcracker
# cp conf/*.yml /etc/nutcracker/
修改配置文件:
# vi /etc/nutcracker/nutcracker.yml
alpha:
listen: 127.0.0.1:22121
hash: fnv1a_64
distribution: ketama
auto_eject_hosts: true
redis: true
server_retry_timeout: 2000
server_failure_limit: 1
servers:
- 127.0.0.1:6380:1
- 127.0.0.1:6381:1
- 127.0.0.1:6382:1
启动nutcracker并使用:
# systemctl start nutcracker
# redis-cli -p 22121
127.0.0.1:22121> set name morris
OK
127.0.0.1:22121> set age 18
OK
127.0.0.1:22121> get name
"morris"
127.0.0.1:22121> get age
"18"
127.0.0.1:22121> key *
Error: Server closed the connection
127.0.0.1:22121> watch name
Error: Server closed the connection
127.0.0.1:22121>