一,四大模式简单实现
1,单机模式
服务器层:一个可连接的redis服务器即可
java层:
配置项:
# spring.redis.host=10.215.208.65
#数据库索引
# spring.redis.database=0
#spring.redis.port=6379
java代码:
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host,port);
redisStandaloneConfiguration.setDatabase(database);
if(!StringUtils.isEmpty(password)){
redisStandaloneConfiguration.setPassword(password);
}
//根据配置和客户端配置创建连接工厂
return new LettuceConnectionFactory(redisStandaloneConfiguration,lettuceClientConfiguration());
2,集群模式
服务器层:多个可连接的redis服务器
java层:
配置项:
# 最大的连接重试次数
spring.redis.cluster.max-redirects=2
# 集群 192.168.74.135:26379,192.168.74.136:26379
spring.redis.sentinel.nodes=10.215.208.104:7000,10.215.208.104:7001,10.215.208.104:7002
java代码:
source.put("spring.redis.cluster.nodes", nodes);
source.put("spring.redis.cluster.max-redirects", maxRedirects);
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(new MapPropertySource("RedisClusterConfiguration", source));
if(!StringUtils.isEmpty(password)){
redisClusterConfiguration.setPassword(password);
}
//根据配置和客户端配置创建连接
lettuceConnectionFactory = new
LettuceConnectionFactory(redisClusterConfiguration,lettuceClientConfiguration());
3,主从模式
服务器层:
一个master服务器,设置Redis服务只监听本机,并将masterport设置为本机的端口号
其他slave服务器,可以是多个,设置Redis服务只监听本机,并将masterip和masterport分别设置为第一台服务器上的ip地址和端口号
$redis-cli slaveof
java层:
String masterHost = "";
String slaves = ",";
RedisStaticMasterReplicaConfiguration configuration = new RedisStaticMasterReplicaConfiguration(
masterHost, this.port);
if(StringUtils.isNotBlank(slaves)){
String[] slaveHosts=slaves.split(",");
for (int i=0;i<slaveHosts.length;i++){
configuration.addNode(slaveHosts[i], this.port);
}
}
return new LettuceConnectionFactory(configuration, lettuceClientConfiguration());
方式二,只配置master节点信息,有服务器配置redis,自动获得slave节点
@Configuration
class WriteToMasterReadFromReplicaConfiguration {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
.readFrom(ReadFrom.SLAVE_PREFERRED)
.build();
RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration("server", 6379);
return new LettuceConnectionFactory(serverConfig, clientConfig);
}
}
4,哨兵模式
RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(
redisProperties.getSentinel().getMaster(), new HashSet<>(redisProperties.getSentinel().getNodes())
);
// RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
// .master("mymaster")
// // 哨兵地址
// .sentinel("192.168.80.130", 26379)
// .sentinel("192.168.80.130", 26380)
// .sentinel("192.168.80.130", 26381);
// 配置读写分离
LettucePoolingClientConfiguration lettuceClientConfiguration = LettucePoolingClientConfiguration.builder()
// 读写分离,这里的ReadFrom是配置Redis的读取策略,是一个枚举,包括下面选择
// MASTER 仅读取主节点
// MASTER_PREFERRED 优先读取主节点,如果主节点不可用,则读取从节点
// REPLICA_PREFERRED 优先读取从节点,如果从节点不可用,则读取主节点
// REPLICA 仅读取从节点
// NEAREST 从最近节点读取
// ANY 从任意一个从节点读取
.readFrom(ReadFrom.REPLICA_PREFERRED)
.build();
return new LettuceConnectionFactory(redisSentinelConfiguration, lettuceClientConfiguration);
java代码:redis其他配置
自定义slave读取规则
LettuceClientConfiguration clientConfig =
LettucePoolingClientConfiguration.builder().readFrom(new ReadFrom() {
@Override
public List<RedisNodeDescription> select(Nodes nodes) {
List<RedisNodeDescription> allNodes = nodes.getNodes();
int ind = Math.abs(index.incrementAndGet() % allNodes.size());
RedisNodeDescription selected = allNodes.get(ind);
logger.info("Selected random node {} with uri {}", ind, selected.getUri());
List<RedisNodeDescription> remaining = IntStream.range(0, allNodes.size())
.filter(i -> i != ind)
.mapToObj(allNodes::get).collect(Collectors.toList());
return Stream.concat(
Stream.of(selected),
remaining.stream()
).collect(Collectors.toList());
}
}).commandTimeout(Duration.ofMillis(timeout))
.poolConfig(genericObjectPoolConfig).build();
return new LettuceConnectionFactory(configuration, clientConfig);
连接配置,客户端配置
private LettuceClientConfiguration lettuceClientConfiguration() {
//连接池配置
GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxTotal(maxActive);
genericObjectPoolConfig.setMaxWaitMillis(maxWait);
genericObjectPoolConfig.setMaxIdle(maxIdle);
genericObjectPoolConfig.setMinIdle(minIdle);
//redis客户端配置
LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder
builder = LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofSeconds(timeOut));
builder.poolConfig(genericObjectPoolConfig);
return builder.build();
}
CacheManager
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认超时时间:30分钟
redisCacheConfiguration.entryTtl(Duration.ofMinutes(30L));
// 如果是空值,不缓存
redisCacheConfiguration.disableCachingNullValues();
// 设置key序列化器
redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
// 设置value序列化器
redisCacheConfiguration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
RedisCacheManager.RedisCacheManagerBuilder build = RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(factory));
RedisCacheManager cacheManager = build.cacheDefaults(redisCacheConfiguration).build();
return cacheManager;
}
四大模式对比:
主从:
优点: 主从结构具有读写分离,提高效率、数据备份,提供多个副本等优点。
不足: 最大的不足就是主从模式不具备自动容错和恢复功能,主节点故障,集群则无法进行工作,可用性比较低,从节点升主节点需要人工手动干预。
普通的主从模式,当主数据库崩溃时,需要手动切换从数据库成为主数据库:
在从数据库中使用SLAVE NO ONE命令将从数据库提升成主数据继续服务。
启动之前崩溃的主数据库,然后使用SLAVEOF命令将其设置成新的主数据库的从数据库,即可同步数据。
哨兵:
核心还是主从模式,不同的是解决主机宕机后的,从slave中自动选择一个slave作为master的机制。
优点
哨兵模式是基于主从模式的,解决可主从模式中master故障不可以自动切换故障的问题。
不足-问题
是一种中心化的集群实现方案:始终只有一个Redis主机来接收和处理写请求,写操作受单机瓶颈影响。
集群里所有节点保存的都是全量数据,浪费内存空间,没有真正实现分布式存储。数据量过大时,主从同步严重影响master的性能。
Redis主机宕机后,哨兵模式正在投票选举的情况之外,因为投票选举结束之前,谁也不知道主机和从机是谁,此时Redis也会开启保护机制,禁止写操作,直到选举出了新的Redis主机。
集群:
优点:
1,解决主从模式的每个节点存储的数据都是全量的数据,数据量过大。大家内容相同存在的浪费内存空间,且数据量过大也会牺牲同步性能。一个master带来的写操作性能瓶颈。
2,客户端sharding技术使用hash一致性算法分片的好处是所有的逻辑都是可控的,不依赖于第三方分布式中间件。服务端的Redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,非常容易线性扩展,系统的灵活性很强。开发人员清楚怎么实现分片、路由的规则,不用担心踩坑。
3,代理分片:中间件作为查找redis服务器的逻辑中间方。Twemproxy,Codis
Redis Cluster
服务器Sharding技术(分片和路由都是在服务端实现),采用多主多从,每一个分区都是由一个Redis主机和多个从机组成,片区和片区之间是相互平行的。Redis Cluster集群采用了P2P的模式,完全去中心化。
虚拟哈希槽分区而非一致性hash算法,预先分配一些卡槽
参考: