数据分片的作用
通过对Redis主从复制和哨兵机制的学习,我们不难看出,这两种机制解决了高可用、高并发读取的问题。但是,当需要进行海量数据存储及高并发数据写入时,单靠这两种机制并不能达到使用要求,于是,就出现了Redis的重要概念:数据分片。
Redis通过数据分片集群机制,将数据分散储存在多个节点上,以提高系统的扩展性和性能;
1.扩展性:当现有的节点无法满足系统的存储及性能需求时,可以通过添加更多节点扩展系统的存储和处理能力。
2.高可用性:每个master节点都可以有多个slave节点,master节点之间通过ping监测彼此健康状态。即使其中某个节点出现故障,系统依旧可以正常工作。
3.负载均衡:Redis分区允许将负载均衡在多个节点上,避免单个节点成为性能瓶颈。通过均匀分布数据和请求负载,确保每个节点处理的数据量和请求量相对平衡,从而提高系统的整体吞吐量和响应能力。
4.数据隔离:通过将数据分区储存在不同的节点上,可以实现逻辑上的数据隔离。不同的数据可以分布在不同的分区中,从而提供更好的数据管理和维护。
实现数据分片的三种方案
1.客户端:通过取模或一致性Hash对Key进行分析,无论是查询还是修改都先判断Key应该路由到的对应的Redis服务节点。意味这每个客户端都要和集群中的每个Redis节点进行连接。当客户端增多时,Redis服务的连接开销也会变大。并且由于节点写死在代码中,无法进行动态配置,且分片逻辑要自行维护。
2.代理Proxy:代理的出现主要是为了解决客户端分片时,Redis连接开销过大以及分片逻辑的统一维护的问题。通过抽取客户端分片的处理逻辑,运行一个独立的代理服务,客户端连接到代理服务,代理服务做请求的转发,可以实现不同客户端、语言复用相同的分片逻辑。常用的代理有Twemproxy、Codis。为了高可用,还需要在客户端和代理之间加一层负载均衡,为保证节点的高可用,还要为额外添加监控来实现节点Ip的自动转移,如LSV+Keepalived。毫无疑问,这样会造成系统架构复杂维护困难的问题,并且要进行扩容缩容时,配置必须要修改,并重新分布数据。
3.服务端:Redis Cluster(官方推荐):以上两种方式,扩展性、新增节点重新分片都是难以避免的问题,这两种方式适合将Redsi作为缓存的时候使用,因为缓存数据的丢失并不重要。但是,当Redis作为数据库使用时,由于要保证数据不会丢失,这种问题是必须要避免的。
Redis Cluster
Redis Cluster(Redis3.0之后)实现了”去中心化“,可以实现在多个节点之间的自动分片,在数据被分片后的分区中,在多个分区之间提供高可用功能。解决了自动故障转移的问题。Redis Cluster是去中心化的,不需要ZK一类的组件进行协调,且客户端可以连接到任意一个节点。
数据分片需要解决的问题和解决方式:
1.分片算法:使数据可以均匀的分布在集群的节点上;
2.路由:使客户端可以访问到正确的节点;
3.故障转移:当节点增减对数据进行分片时,保证客户端可以正常访问服务。
Redis Cluster搭建
Redis Cluster可以认为是多个redis节点组成的数据集合,客户端连接到Redis集群时,不需要关注数据到底在哪个节点上,只需要连接任意一个或多个节点,并关注集群的整体即可。每两个节点之间会两两交互,共享数据的分片以及节点的状态信息等。
单机安装示例:由于redis可以通过redis.conf启动多个服务,所以可以直接通过修改配置文件中端口号,从而启动多个服务。
注意:Redis 集群节点数量至少为 6 个才能保证组成完整高可用的集群。每个节点需要开启配置 cluster-enabled yes,让 Redis 运行在集群模式下。
1.创建文件夹
cd redis-5.0.5
mkdir redis-cluster
cd redis-cluster
mkdir 7291 7292 7293 7294 7295 7296
2.复制配置文件
cp ../redis.conf 7291
vim 7291/redis.conf
3.需要对以下参数进行修改
port 7291
daemonize yes
#打开集群模式
cluster-enabled yes
#设定节点配置文件名
cluster-config-file modes-7291.conf
#设定节点失联时间(毫秒),超过该时间,集群自动进行主从切换
cluster-node-timeout 15000
#打开aof数据持久化
appendonly yes
#当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis_7291.pid
#rdf保存路径
dir /usr/local/soft/redis-5.0.5/redis-cluster/7291
外网集群需要添加以下配置
# 实际给各节点网卡分配的IP(公网IP)
cluster-announce-ip xx.xx.xx.xx
# 节点映射端口
cluster-announce-port ${PORT}
# 节点总线端口
cluster-announce-bus-port 1${PORT}
4.批量复制并修改内容
cp redis.conf ../7292
……
sed -i 's/7291/7292/g' 7292/redis.conf
……
5.启动节点
./src/redis-server redis-cluster/7291/redis.conf
6.创建集群,注意要使用绝对IP。
--cluster-replicas 1
表示每个主库下有1个从库
redis-cli --cluster create [ip]:[port] [ip]:[port]... --cluster-replicas 1
Redis会给出一个预计的方案,对6个节点分配3主3从,如下图,如果认为没有问题,输入yes确认
以下内容表示集群创建成功。
集群测试
通过Jedis向集群输入2000个key
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
jedisClusterNodes.add(new HostAndPort("192.168.2.128", 7291));
JedisCluster jedis = new JedisCluster(jedisClusterNodes);
for (int i = 0; i < 2000; i++) {
jedis.set("cluster"+i, String.valueOf(i));
}
执行结束后,我们可以看到,数据相对均匀的分布到了三个节点上。
RedisCluster原理
在redis-cluster中,有一个虚拟槽(slot)的概念;在一个集群架构中,会创建一个
2
14
2^{14}
214(即16384)个slot。每个区间的槽会分配给redisGroup。执行set命令时,传入的值通过CRC16对KEY进行计算,再对16384取模,得到的余数,获取对应下标的区间及负责的redisGroup。使得数据可以相对均匀的分布到各个节点上。
redisGroup通过一个长度为16384的位序列,用0、1标识相应下标槽是否归当前的redisGroup负责。
通过cluster keyslot [key]
可以获得KEY对应的slot序号。
在key中加上一个{hashTag},这样的话CRC16只会计算{}中的值。这样,可以使相关的数据集中到一个节点上。
直接在redis-cli执行set命令时,会出现MOVD IP:PORT的报错,这是因为key不归当前节点处理,在进入redisCli时,通过redis-cli -c -p [prot]
进入,当set的Key不归当前节点处理时,会进行自动重定向。
在集群模式下,当主节点故障时,集群中的其他节点会监测到主节点宕机的情况,然后从这些节点中选出一个新的主节点。
新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。并向集群广播一条PONG消息,让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。
通过使7291节点关闭模拟主节点下线,使用cluster slots
命令,我们看到了这一变化。