springBoot整合reids
1.改pom文件
添加jar包
<!--spring整合redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
2.测试单机连接
package com.jt.test;
import com.baomidou.mybatisplus.annotation.TableId;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.params.SetParams;
//@SpringBootTest //如果需要在测试类中引入spring容器机制才使用该注解
public class TestRedis {
/**
* 测试远程redis服务器是否可用
* host: 192.168.126.129
* port: 6379
* 思路:
* 1.实例化链接对象
* 2.利用对象执行redis命令
*
* 报错调试:
* 1.检查Redis.conf的配置文件是否按照要求修改 ip/保护/后台
* 2.redis启动方式 redis-server redis.conf
* 3.关闭防火墙 systemctl stop firewalld.service
* */
@Test
public void test01(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.set("redis", "测试redis是否可用");
System.out.println(jedis.get("redis"));
}
/**
* String类型API学习
* 需求: 判断key是否存在于Redis.如果存在则不赋值,否则入库.
*
*/
@Test
public void test02(){
Jedis jedis = new Jedis("192.168.126.129",6379);
if(jedis.exists("redis")){
System.out.println("数据已存在");
jedis.expire("redis", 10);
}else{
jedis.set("redis", "aaaa");
}
System.out.println(jedis.get("redis"));
}
//可以利用优化的API实现业务功能.
//业务: 如果数据存在则不赋值
@Test
public void test03(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.flushAll(); //清空redis服务器
// 如果key存在则不做任何操作
jedis.setnx("redis", "测试赋值操作!!!");
System.out.println(jedis.get("redis"));
}
/**
* 测试添加超市时间的有效性.
* 业务: 向redis中保存一个数据之后,要求设定10秒有效.
* 原子性: 要么同时成功,要么同时失败.
*/
@Test
public void test04(){
Jedis jedis = new Jedis("192.168.126.129",6379);
/* jedis.set("aa", "aa"); //该数据将永不删除.
int a = 1/0;
jedis.expire("aa", 10);*/
jedis.setex("aa", 10, "aa"); //单位秒
//jedis.psetex(, ); //设置毫秒
}
/**
* 需求: 要求添加一个数据,只有数据存在时才会赋值,并且需要添加超时时间
* 保证原子性操作.
* private static final String XX = "xx"; 有key的时候才赋值
* private static final String NX = "nx"; 没有key时才赋值
* private static final String PX = "px"; 毫秒
* private static final String EX = "ex"; 秒
* redis分布式锁的问题
* */
@Test
public void test05(){
Jedis jedis = new Jedis("192.168.126.129",6379);
SetParams setParams = new SetParams();
setParams.xx().ex(10);
jedis.set("aaa", "aaa", setParams);
}
@Test
public void testList(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.lpush("list2", "1,2,3,4,5");
System.out.println(jedis.rpop("list2"));
}
/**
* 控制redis事务
* 说明:操作redisredis适用于事务控制
* 但是如果是多台redis则不太适用事务.
* */
@Test
public void testTx(){
Jedis jedis = new Jedis("192.168.126.129",6379);
//开启事务
Transaction transaction = jedis.multi();
try {
transaction.set("bb", "bb");
transaction.exec(); //提交事务
}catch (Exception e){
transaction.discard();
}
}
}
创建配置类
目的,项目的域名和端口号解耦
package com.jt.config;
importorg.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.Jedis;
@Configuration //标识我是一个配置类
@PropertySource("classpath:/properties/redis.properties")
public class JedisConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
/**
* 将jedis对象交给spring容器管理
*/
@Bean
public Jedis jedis(){
//由于将代码写死不利于扩展,所以将固定的配置添加到配置文件中
return new Jedis(host,port);
}
}
redis分片机制
为什么使用分片
当redis需要存储海量的数据存储,单个reids显然不能完成这个业务,如果一直扩容redis的内存,效果并不好,这时候就需要进行分片,开启多台redis,分别保存用户的数据,实现内存扩容
配置redis分片
在Linux系统中,给分片备好专门的文件夹,在redis根目录中创建一个shards目录
mkdir shards
由于redis启动时更具配置文件redis.conf 运行的,所以需要多少台reids就需要复制几份配置文件到 shards目录.
将复制好的配置文件的端口号进行修改
修改好之后依次启动,也可以使用 sell脚本启动,这样就是分片机制,既多开
springboot测试
/**
* 测试Redis分片机制
*/
@Test
public void testShards(){
//泛型是redis提供的类型,专门用于存redis服务
List<JedisShardInfo> list = new ArrayList<>();
//分别将多台reids添加进容器
list.add(new JedisShardInfo("192.168.126.129", 6379));
list.add(new JedisShardInfo("192.168.126.129", 6380));
list.add(new JedisShardInfo("192.168.126.129", 6381));
//构造分片服务器,使用默认的一致性hash算法进行数据的存储
ShardedJedis shardedJedis = new ShardedJedis(list);
shardedJedis.set("2005", "redis分片学习");
System.out.println(shardedJedis.get("2005"));
}
分片的 一致性hash算法
什么是一致性hash算法
是一种特别的哈希算法,更具hash的取值范围,获取node节点: 2^32冥,从开始到结尾形成一个圈,有多少台redis的个数获取多少个reids的节点,将每个存入的数组的key进行hash计算,得到的hash值按照顺时针反向找到最近的redis服务节点存入
一致性hash算法的特性
平衡性:hash的结果应该平均分配到各个节点,从算法上解决负载均衡
说明.如果发现节点中存储的数据负载不均衡,会启用虚拟节点的方式实现数据的平衡
单调性:指在新增或者删除节点时,不影响系统的正常运行,会自动实现数据转移
分散性:指数据应该分散的存放在各个节点中,没有必要每个节点都存储所有的数据
分片的缺点
功能主要的作用是实现内存数据的扩容,但是如果宕机了不能实现高可用
springboot整合redis分片
#添加redis的配置
#添加单台配置
#redis.host=192.168.126.129
#redis.port=6379
#配置多台的redis信息
redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381
redis主从实现
为了实现redis高可用的前提条件,可以实现主redis宕机后从redis服务器顶上去
准备工作
构建一个专用的目录用于实现主从配置,避免混淆
mkdir sentinel
向文件中复制好配置文件
修改好端口号,并启动
搭建 进入到第一台redis服务器
使用命令检查主从状态
info replication
role 显示 master 说明是主机
进入准备做从机的redis服务器使用命令进行主从挂载
slaveof 主机IP 主机端口
检查是否是从机
role显示是 slave, 表示是从机
master_host:显示主机的ip
master_port:显示主机的端口号
主从挂载错误说明
当挂载错误后,可以将reids服务器全部关闭,之后再重启,默认条件下主从挂载会失效,重新挂载即可,因为主从挂载的命令只做用与内存中.
需要永久有效,需要写到配置文件中
redis的哨兵机制
为了实现redis服务的高可用,解决主机宕机后由那个从机顶上
哨兵工作原理
-
当哨兵启动时,会监控当前的主机信息,同时获取连接当前主机的从机信息
-
哨兵会使用心跳检查机制监控主机是否宕机,如果主机连续3次没有响应,则认为主机宕机,开始选举机制挑选一个从机当主机
-
当新的主机被确定,其他从机将会成为新主机的从机,当之前主机恢复后,也会成为从机
配置哨兵机制
准备 : 将原有哨兵配置文件sentinel.conf 复制一份到之前准备好的文件夹中,尽量不修改原配置文件,避免配置文件写错后,没有备用
修改哨兵的配置文件,将保护模式关闭
开启后台运行
修改哨兵的监控,其中的一表示投票数为1就生效,成为主机
修改哨兵选举时间 , 意思是主机宕机10s 后开启进行选举
修改哨兵选举超时时间
保存设置,并启动哨兵,进行测试
- 启动哨兵命令:
redis-sentinel sentinel.conf
- 先关闭主机,之后等待10秒之后,检查从机是否当选主机.之后再次启动主机(宕机的),检查是否为新主机的从
关于哨兵的说明
哨兵的脑裂现象:
多个哨兵进行选举,如果连续3次投票失败,可能会引发脑裂,既多个主机
出现脑裂的几率为12.5%,
解决方法:增加选举的节点的数据,使用概率论
/**
* 哨兵的测试
* 参数说明: masterName: 主机的变量名称
* sentinels: 链接哨兵的集合.
* 理解: 哨兵虽然链接3台redis. 但是3台redis中存储的数据都是一样的.对于用户而言
* 就是一台.
*/
@Test
public void testSentinel(){
Set<String> sets = new HashSet<>();
sets.add("192.168.126.129:26379");
//哨兵池,获取哨兵对象
JedisSentinelPool sentinelPool =
new JedisSentinelPool("mymaster",sets);
//获取redis主机的服务对象
Jedis jedis = sentinelPool.getResource();
jedis.set("aaa", "wt");
System.out.println(jedis.get("aaa"));
jedis.close();
}
reids集群
目的:整合分片和哨兵的功能
为了保证数据分散存储,同时保证数据存储的一致性.并且在内部实现高可用的机制.实现了服务故障的自动迁移.
准备 搭建最低配置
主从划分: 3台主机 3台从机共6台 端口划分7000-7005
1.准备一个专业文件夹 cluster 命令
Mkdir cluster
2.在cluster文件夹中分别创建7000-7005文件夹
mkdir 7000 7001 7002 7003 7004 7005
3.将redis根目录中的redis.conf文件复制到cluster/7000/ 并以原名保存
cp redis.conf cluster/7000/
编辑7000文件夹中的redis.conf文件
- 注释本地绑定的ip地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jWO0ewH5-1602686255409)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173205989.png)]
- 关闭保护模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GohvIb1m-1602686255411)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173240428.png)]
- 修改端口号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EFvZ5O3K-1602686255413)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173304115.png)]
- 启动后台启动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AyZy6IPM-1602686255415)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173324221.png)]
- 修改pid文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-heH4WYNo-1602686255417)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173351030.png)]
- 修改持久化文件路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9N8eL70s-1602686255419)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173409711.png)]
- 设定内存优化策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IeuU1PuZ-1602686255420)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173427307.png)]
- 关闭AOF模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yn6QFNvy-1602686255421)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173450855.png)]
- 开启集群配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uJI8EoLl-1602686255422)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173503607.png)]
-
开启集群配置文件
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hx9ZRUtl-1602686255423)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173521563.png)]
-
修改集群超时时间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YyCVp1UO-1602686255424)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013173537424.png)]
修改完毕保存
3.说明:将7000文件夹下的redis.conf文件分别复制到7001-7005中
[root@localhost cluster]# cp 7000/redis.conf 7001/
[root@localhost cluster]# cp 7000/redis.conf 7002/
[root@localhost cluster]# cp 7000/redis.conf 7003/
[root@localhost cluster]# cp 7000/redis.conf 7004/
[root@localhost cluster]# cp 7000/redis.conf 7005/
批量修改 配置文件
说明:分别将7001-7005文件中的7000改为对应的端口号的名称,
修改时注意方向键的使用
进入reids.conf文件中使用 %s/7000/7001/g 批量修改
vim 7000/redis.conf
%s/7000/7001/g
通过脚本启动
编辑脚本编辑启动脚本命令
vim start.sh
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QPmrzhQP-1602686255427)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013190517008.png)]
编辑关闭脚本 命令
vim shutdown.sh
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uLehjrK0-1602686255428)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013190624178.png)]
使用脚本启动,命令
sh start.sh
检查redis节点启动是否正常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7dwL4Ox9-1602686255429)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013190755369.png)]
创建redis集群
重启reids服务并搭建集群
#5.0版本执行 使用C语言内部管理集群
redis-cli --cluster create --cluster-replicas 1 192.168.35.130:7000 192.168.35.130:7001 192.168.35.130:7002 192.168.35.130:7003 192.168.35.130:7004 192.168.35.130:7005
redis集群说明
存储的原理
hash槽算法也叫分区算法
根据哈希算法中的CRC16[key]%16384 进行映射存储,一共16384个槽位
当向redis集群中插入数据时,首先将key进行计算.之后将计算结果匹配到具体的某一个槽的区间内,之后再将数据set到管理该槽的节点中.
Redis集群高可用推选原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81TKpFsx-1602686255430)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013190945894.png)]
rredis的所有节点都会保存当前redis集群中全部主从状态信息,并且每个节点都能够相互通信,当一个节点发生宕机现象,则集群中的其他节点通过ping-pong检测机制检查redis节点是否宕机,当半数以上的节点认为宕机,则认为主节点宕机,同时由redis剩余的主节点进入选举机制,投票选举链接宕机的主节点的从机.实现故障迁移
redis集群宕机条件
特点:集群中如果主机宕机,那么从机可以继续提供服务,
当主机中没有从机时,则向其它主机借用多余的从机.继续提供服务.如果主机宕机时没有从机可用,则集群崩溃.
答案:9个redis节点,节点宕机5-7次时集群才崩溃.既半数以上宕机
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0DdDcyOu-1602686255431)(C:\Users\1\AppData\Roaming\Typora\typora-user-images\image-20201013191758399.png)]
Redis内存优化
LRU算法:即最近最少使用是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
维度:时间T
LRU算法是当下实现内存清理的最优算法.
LFU算法即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
维度: 引用次数
Random算法 随机删除
TTL算法说明:监控剩余的存活时间,将存活时间少的数据提前删除.
在redis.conf配置文件中设置
1.volatile-lru 在设定了超时时间的数据中,采用lru算法.
2.allkeys-lru 所有数据采用lru算法
3.volatile-lfu 在超时的数据中采用lfu算法
4.allkeys-lfu -> 所有数据采用lfu算法
5.volatile-random -> 设定超时时间的数据采用随机算法
6.allkeys-random -> 所有数据随机删除
7.volatile-ttl -> 删除存活时间少的数据
8.noeviction -> 不会删除数据,如果内存溢出报错返回.
面试题
假设你是项目组长,手下一个特别漂亮的妹子在线上执行了flushALL命令.问:如果是你应该怎么办??
解决方案: 应该将AOF模式中flushAll删除,之后重启redis即可.
redis的持久化机制
RDB:
但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
维度: 引用次数
Random算法 随机删除
TTL算法说明:监控剩余的存活时间,将存活时间少的数据提前删除.
在redis.conf配置文件中设置
1.volatile-lru 在设定了超时时间的数据中,采用lru算法.
2.allkeys-lru 所有数据采用lru算法
3.volatile-lfu 在超时的数据中采用lfu算法
4.allkeys-lfu -> 所有数据采用lfu算法
5.volatile-random -> 设定超时时间的数据采用随机算法
6.allkeys-random -> 所有数据随机删除
7.volatile-ttl -> 删除存活时间少的数据
8.noeviction -> 不会删除数据,如果内存溢出报错返回.
面试题
假设你是项目组长,手下一个特别漂亮的妹子在线上执行了flushALL命令.问:如果是你应该怎么办??
解决方案: 应该将AOF模式中flushAll删除,之后重启redis即可.
redis的持久化机制
RDB:
AOF: