文章目录
-
-
-
- 1、为什么需要用redis集群
- 2、主从复制
-
- a、主从复制配置
- 3、可用性保证之哨兵机制(Sentinel)
- 4、redis分布式方案
-
- a、客户端Sharding
- b、代理Proxy之Twemproxy
- c、代理Proxy之Codis
- d、Redis Cluster
- e、分片方案总结
-
-
1、为什么需要用redis集群
老套路,性能、扩展、可用性。下面几个说法就当故事听听了。
性能
- 第一个是因为Redis 本身的QPS已经很高了,但是如果在一些缤纷咖喱昂非常高的情况下,性能还是会受到影响。这个时候我们希望有更多的redis服务来完成工作。
扩展
- 第二个是出于存储的考虑。因为redis所有的数据都放在内存中,如果数据量大,很容易受到硬件的限制。升级硬件收效和成本比太低,所以我们需要有一种横向扩展的方法。
可用性
- 第三个是可用性和安全的问题了。如果只有一个redis服务,一旦服务宕机,那么所有的客户端都无法访问,会对业务造成很大的影响。另一个,如果硬件发生故障,而单机的数据无法恢复的话,带来的影响也是灾难性的。
可用性、数据安全、性能…。。。。是在编写不下去了。。。其实就几句话
- 一台性能有限、所以只能扩展多台,扩展多台,就涉及到分布式集群问题,自然就延伸出cap理论了。就这么简单。真的是个人都喜欢去改个词 弄的千奇百怪。。哎。。
2、主从复制
a、主从复制配置
只是针对主从模式下,例如加入了sentinel 的主从关系。没有这样的中间件帮忙。自身切换很麻烦。
redis -cluster 集群模式不能使用主从复制相关命令
- 会报错:(error) ERR REPLICAOF not allowed in cluster mode.
配置方法
- 一种:在每个slave节点的redis.conf配置文件增加一行
-
slaveof 192.168.20.202 6379
-
- 二种:启动时候通过启动参数指定master节点
-
redis-server --slaveof 192.168.20.202 6379
-
切换变化
- 主从切换
-
这样自己就变成从了slaveof xx xx
-
- 主从切换的时候,这个配置会被重写成
-
# slaveof 192.168.20.202 6379 # Generated by CONFIG REWRITE replicaof 192.168.20.202 6379
-
- 查看集群状态
-
info replication
-
- 断开主从复制
-
此刻就断开联系了。不再复制数据slaveof no one
-
3、可用性保证之哨兵机制(Sentinel)
就是在主库故障了。重新选举主节点 ; 分别是断开连接时长、优先级排序、复制数量、进程id 来决定选举结果。Sentinel是没有主从只分,但是当redis集群master挂了,会采用Ratf算法选择一个Sentinel临时主节点去做主从切换
《快速搭建一主二从Sentinel监控配置》
功能描述:
- 监控:Sentinel 会不断检查主服务器和从服务器是否正常运行
- 通知:如果某一个被监控的实例出现问题,Sentinel可以通过API发出通知
- 自动故障转移(failover):如果主服务器发生故障,Sentinel可以启动故障转移过程。把某台服务器升级为主服务器,并发出通知
- 配置管理:客户端连接到Sentinel,获取当前的Redis主服务器的地址
4、redis分布式方案
所有客户端连接一个proxy或者用分区框架来实现数据分片(存储在不同redis服务器)
a、客户端Sharding
采用2个独立的redis 然后用Sharding来操作测试
使用 ShardedJedis 之类的客户端分片代码的优势是配置简单,不依赖于其他中间
件,分区的逻辑可以自定义,比较灵活。但是基于客户端的方案,不能实现动态的服务
增减,每个客户端需要自行维护分片策略,存在重复代码。
第二种思路就是把分片的代码抽取出来,做成一个公共服务,所有的客户端都连接
到这个代理层。由代理层来实现请求和转发。
示例:
package org.example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import java.util.ArrayList;
import java.util.List;
@RunWith(JUnit4.class)
public class ShardingTest {
@Test
public void test() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
// redis服务器
JedisShardInfo shardInfo1 = new JedisShardInfo("192.168.170.3", 1000);
JedisShardInfo shardInfo2 = new JedisShardInfo("192.168.170.3", 1001);
// 连接池
List<JedisShardInfo> infoList = new ArrayList<>(2);
infoList.add(shardInfo1);
infoList.add(shardInfo2);
ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, infoList);
try (ShardedJedis shardedJedis = jedisPool.getResource()) {
for (int i = 0; i < 10000; i++) {
shardedJedis.set("k" + i, "" + i);
}
for (int i = 0; i < 10000; i++) {
shardedJedis.get("k" + i);
}
} catch (Exception e) {
throw e;
}
}
}
b、代理Proxy之Twemproxy
twitter 开源的。two-em-proxy
比较稳定,性价比高
出现故障不能自动转移,架构复杂,需要借助其他组件(LVS/HAproxy+Keepalived)实现HA
扩缩容需要修改配置,不能实现平滑地扩缩容(需要重新分布数据)。
c、代理Proxy之Codis
国内豌豆荚开源的Codis,是一个代理中间件,由Go语言开发
codis不支持的命令:https://github.com/CodisLabs/codis/blob/release3.2/doc/unsupported_cmds.md
分片原理:Codis把所有的key分成了N个槽(例如1024),每个槽对应一个分组,一个分组对应于一个或者一组 Redis 实例。Codis 对 key 进行 CRC32 运算,得到一个32位的数字,然后模以N(槽的个数),得到余数,这个就是key对应的槽,槽后面就是Redis的实例。比如4个槽:
Codis的槽位映射关系是保存在 Proxy中的,如果要解决单点的问题,Codis 也要做集群部署, 多个Codis节点怎么同步槽和实例的关系呢?需要运行一个Zookeeper (或者etcd/本地文件)
d、Redis Cluster
3.0开始正式推出的,同时也可以实现高可用,跟Codis不一样,它是去中心化的。客户端可以连接到任意一个可用节点。
Redis既没有用哈希取模,也没有用一致性哈希,而是用虚拟槽来实现的。
Redis创建了16384个槽(slot),每个节点负责一定区间的slot。比如Node1负
责0-5460,Node2负责5461-10922,Node3负责10923-16383
注意:key与slot的关系是永远不会变的,会变的只有slot和Redis节点的关系。
《1 秒搭建Redis-Cluster集群》
- 查看key属于那个slot
cluster keyslot lilei
- 多key操作的hash tags{}
multi key 操作是不能跨节点的,所以要让数据分布到一个节点,就可以如下:
# 这样{} 的标志,让他们都会落在桶一个slot里面 set a{qs}a 1 set a{qs}b 1 set a{qs}c 1 set a{qs}d 1 set a{qs}e 1
- 客户端重定向
在使用redis-cli时,可以加上-c参数,这样redis会自动帮我们连接到正确的节点执行命令。
redis-cli -p 7292 -c
- 数据迁移
因为key 和slot的关系是永远不会变的,当新增了节点的时候,需要把原有的slot 分配给新的节点服务,并且把相关的数据迁移过来。
添加新节点(新增一个7297)
新增的节点没有哈希槽,不能分布数据,在原来的任意一个节点上执行:redis-cli--cluster add-node 127.0.0.1:7291 127.0.0.1:7297
redis-cli--cluster reshard 127.0.0.1:7291
- 高可用和主从切换原理
当slave 发现自己的master 变为fail状态时,便尝试进行failover,以期成为新的master。由于挂掉的master可能会有多个slave从而存在多个slave竞争成为master节点的过程。
自动实现主从角色分配,主从自动切换
过程如下:
1、slave发现自己的master 变为fail
2、将自己记录的集群currentEpoch 加1,并广播FAILOVER_AUTH_REQUEST 信息
3、其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每个epoch只发送一次ack
4、尝试failover的slave手机FAILOVER_AUTH_ACK
5、超过半数后变成新Master
6、广播Pong通知其他集群节点
e、分片方案总结
功能 | Codis | Tewmporyx | Redis Cluster |
---|---|---|---|
重新分片不需要重启 | Yes | NO | Yes |
pipeline | Yes | Yes | |
多key操作的hash tags{} 让命令落在同一节点 | Yes | Yes | Yes |
重新分片时的多key操作 | Yes | - | NO |
客户端支持 | 所有 | 所有 | 支持cluster协议的客户端 |