1 Redis持久化策略
1.1 策略说明
说明:
redis作为内存数据库,需要保持大量用户的数据。但是redis中的数据保存在内存中,一旦遇上断电/宕机,肯定会导致内存数据丢失。
所以为了防止数据丢失,redis采用持久化的方式,实现内存数据的备份。
策略说明:
当redis内存中有数据时,自身支持两种策略模式:
- RDB模式
- AOF模式
redis根据相应模式的设置进行数据持久化,且RDB模式和AOF模式都有各自的持久化文件。当redis重启时,redis会根据配置文件中指定的持久化文件名称,加载持久化文件,为内存恢复数据。
1.2 RDB模式
1.2.1 模式说明
RDB模式定期将内存中的数据持久化。
如果用户允许丢失少量的数据则首选RDB模式,因为RDB模式定期为内存做快照,该方式的备份速度很快。
1.2.2 备份命令
**说明:**执行redis备份命令需要在redis客户端执行。
命令:
手动保存
save
后台备份
bgsave
区别:
save 会造成线程阻塞,只有当备份完成时,才允许执行redis更新操作。
bgsave 表示后台运行,不会造成线程阻塞,挑当前不忙的时间做数据备份。
1.2.3 备份方式
save 900 1
在900秒内,redis做一次更新操作,则备份一次
save 300 10
在300秒内,redis做10次更新操作,则备份一次
save 60 10000
在60秒内,如果redis做10000次操作则备份一次
1.2.4 配置文件说明
1.持久化文件名称
2.持久化文件保存路径
dir ./
表示当前文件夹,建议使用绝对路径。
1.3 AOF模式
1.3.1 模式说明
AOF模式可以做到实时数据备份,该模式相当于用配置文件记录了用户的全部操作过程。
特点:
- 可以实时备份数据,安全性好
- 持久化速度较RDB模式更慢
- AOF模式的持久化文件的体积会很大
- 当数据恢复时,需要消耗很长时间
- 持久化文件明文保存,没有加密
1.3.2 AOF模式持久化策略
1.开启AOF模式
注意:开启AOF模式后,RDB模式不生效。
2.AOF模式持久化文件保存路径
dir ./
3.持久化策略
# appendfsync always 每次操作都会备份
appendfsync everysec 每秒备份一次
# appendfsync no 不做操作
2 Redis内存策略
2.1 需求分析
question:
为了保证服务器正常运行,需要为redis设定最大内存空间,但是如果数据一直新增,内存很快就会占满,如何优化内存?
解决方案:
采用LRU算法,将内存中最近最不常用的数据清空,保存其他数据。
2.2 内存优化手段
1.设定内存空间
建议内存不要超过1G,通常256~512M足矣。
修改redis配置文件第537行
2.设定合适的内存策略
可选的内存策略:
-
volatile-lru
在已经设定超时时间的数据中,采用LRU算法进行删除。 -
allkeys-lru
在所有key中,采用LRU算法进行删除。 -
volatile-random
在已经设定超时时间的数据中,随机删除。 -
allkeys-radom
在所有key中,随机删除 -
volatile-ttl
在已经设定超时时间的数据中,根据TTL机制排序,将马上要过期的数据删除。 -
noviction
如果采用该机制,那么内存数据不会删除,将报错消息返回给用户。
策略设定:
2.3 缓存使用问题
1.缓存穿透
条件:访问一条不存在的数据
说明:当访问一个不存在的数据时,因为缓存中没有这个key,导致缓存形同虚设,最终访问后台数据库,但数据库中也没有该数据所以返回null。
隐患:如果有人恶意频繁查询一个不存在的数据,可能会导致数据库负载过高而宕机。
总结:业务系统访问一个不存在的数据,称之为缓存穿透。
防护:限定同一IP访问频率,如京东。
2.缓存击穿
条件:当一条缓存失效/过期/未命中时,高并发访问该key
说明:如果给一个key设置了失效时间,当key失效时有一万的并发请求访问这个key,此时缓存失效,所有的请求都会访问后台数据库,称之为缓存击穿。
场景:微博热点消息访问量很大,如果该缓存失效则会直接访问后台数据库,导致后台数据库负载过高。
3.缓存雪崩
前提:高并发访问,缓存命中较低或者失效时。
说明:假设缓存都设置了失效时间,在同一时间内缓存大量失效,如果这时用户高并发访问,缓存命中率过低,导致全部的用户访问都会访问后台真实的数据库。
场景:在高并发情况下,缓存动态更新时。
3 Redis分片技术
3.1 需求分析
1.如果将全部数据都保存到一台tomcat中,如果该服务器损坏,则将影响全部服务。
2.使用单台redis内存设定一般不要超过1G,但有些业务数据量很大,如果不修改内存则无法存储。
3.2 改进方案
方案:采用Redis分片技术实现。
优点:
- 使用redis分片可以实现内存数据的动态扩容。
- 使用分片,每台redis节点中尽可能存放1/n的数据,防止数据丢失。
- 对于用户而言,整个redis分片就是一个服务。
3.3 分片搭建
3.3.1 复制配置文件
说明:将redis.conf文件复制到shards中,并且复制3份名称为:
- redis-6379.conf
- redis-6380.conf
- redis-6381.conf
3.3.2 修改端口号
说明:分别修改6380/6381的配置文件为对应端口
3.3.3 重启Redis服务
3.3.4 分片测试
@Test
public void testShard(){
/**
* 创建分片的对象
* 1.poolConfig 标识池的大小
* 2.shards redis分片的节点信息
*/
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(1000);
poolConfig.setTestOnBorrow(true); //获取链接时先检测
List<JedisShardInfo> shards = new ArrayList<>();
shards.add(new JedisShardInfo("192.168.126.166",6379));
shards.add(new JedisShardInfo("192.168.126.166",6380));
shards.add(new JedisShardInfo("192.168.126.166",6381));
ShardedJedisPool pool =
new ShardedJedisPool(poolConfig, shards);
//获取redis链接
ShardedJedis jedis = pool.getResource();
jedis.set("shards","保存分片数据");
System.out.println(jedis.get("shards"));
//将链接还回池中
pool.returnResource(jedis);
}
3.4 Hash一致性算法
3.4.1 数据如何保存
1.利用节点的IP+算法确定唯一的哈希值,之后在内存中确定节点的位置。
2.当保存数据时,根据key进行hash运算,确定唯一的一个位置。
3.根据当前key位置顺时针查找最近的node节点进行挂载。
3.4.2 均衡性
说明:根据redis节点的数量尽可能让数据均匀地分片,每个节点中的数据尽可能趋向1/n。
提升:为了保证均衡性,哈希一致性算法引入了虚拟节点的概念。为了平衡数据,如果一个节点不够则生成多个节点。
3.4.3 单调性
说明:如果node节点的新增,能动态地实现数据的迁移,这种特性被称之为单调性。
3.4.4 分散性
特点:由于分布式的原因,用户在使用内存时,不能够获取全部的内存空间,导致一个key对应多个位置。
3.4.5 负载
说明:负载是从另一个角度考虑分散性。
特点:一个位置对应多个key
如何解决:在进行架构设计时,要求使用全部的内存空间。
4 Spring整合分片
4.1 编辑pro文件
redis.host=192.168.126.166
redis.port.a=6379
redis.port.b=6380
redis.port.c=6381
redis.maxTotal=1000
4.2 编辑Spring配置文件
<!--实现spring整合分片 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxTotal}"/>
</bean>
<!--定义三个redis节点 -->
<bean id="shardInfoA" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.a}"/>
</bean>
<bean id="shardInfoB" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.b}"/>
</bean>
<bean id="shardInfoC" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.c}"/>
</bean>
<!--定义连接池 -->
<bean id="jedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg name="poolConfig" ref="poolConfig"/>
<constructor-arg name="shards">
<list>
<ref bean="shardInfoA"/>
<ref bean="shardInfoB"/>
<ref bean="shardInfoC"/>
</list>
</constructor-arg>
</bean>
4.3 编辑工具API
@Service
public class RedisService {
//有的工程需要,有的工程不需要。设置required=false,有就注入,没有就不注入。
@Autowired(required = false)
private ShardedJedisPool shardedJedisPool;
public void set(String key,String value){
ShardedJedis jedis =
shardedJedisPool.getResource();
jedis.set(key, value);
shardedJedisPool.returnResource(jedis);
}
//添加超时时间
public void set(String key,String value,int seconds){
ShardedJedis jedis =
shardedJedisPool.getResource();
jedis.setex(key, seconds, value);
shardedJedisPool.returnResource(jedis);
}
//编辑get方法
public String get(String key){
ShardedJedis jedis =
shardedJedisPool.getResource();
String result = jedis.get(key);
shardedJedisPool.returnResource(jedis);
return result;
}
}
4.4 编辑业务层方法
1.修改自动注入对象
2.切换业务方法
5 Redis哨兵
5.1 业务需求
采用redis分片技术可以实现内存扩容,但是如果其中一台机器宕机,则整个redis分片将不能正常运行。
5.2 实现主从挂载
角色划分:
6379(主机)
6380(从机)
6381(从机)
1.检查节点状态
命令:
info replication
2.实现主从挂载
命令:
127.0.0.1:6380> SLAVEOF 192.168.126.166 6379
3.检查状态
5.3 哨兵实现
5.3.1 哨兵原理
- 哨兵监控主机的状态,并获取主机的全部信息,包括从机的信息。
- 通过心跳检测机制,如果3次连接没有响应,则断定主机宕机了。此时哨兵会从从机中选举一台从机充当从机,同时将另外的从机挂载到现在的主机。
- 当旧的主机修复完成后,继续充当从机,等待下次选举。
5.3.2 编辑哨兵配置文件
1.以原名保存文件
cp sentinel.conf sentinel
2.关闭保护模式
3.修改哨兵监听节点信息
sentinel master mymaster 127.0.0.1 1
说明:
mymaster 表示主机变量名称
IP:端口 表示主机信息
2 表示选举票数
4.修改推选时间
5.修改推选失败时间
5.3.3 哨兵测试
1.启动哨兵
redis-sentinel sentinel.conf
2.启动测试
3.关闭主机,检查哨兵能否推选