Redis

tech2024-04-05  58

1、NoSQL 简介

NoSQL (Not Only SQL),不仅仅是 SQL ,泛指 非关系型的数据库

RDBMS :关系型数据库NoSQL :非关系型数据库

2、什么是 Redis

Redis 是基于内存数据存储,被用作为 数据库、缓存、消息中间件

> Redis 是一个内存型的数据库。 为什么数据存储在内存(读写快、断电立即消失)中,还会作为数据库? 因为 Redis 有持久化的机制,内存中的数据会定期写入到磁盘中。

3、Redis 的特点


Redis 是一个高性能的 key/value 内存型的数据库Redis 支持丰富的数据类型 key value(String、List、Set、ZSet、Hash)Redis 支持持久化【内存数据持久到硬盘中】Redis 单进程、单线程 【效率高】 Redis 可以实现分布式锁(分布式锁,当服务部署在不同的服务器上的时候,锁的问题:之前的 Synchronized 只能在单服务器中有作用。)

4、Redis 的安装

1. 下载完整的源码包 2. 将 redis 的源码包上传到 linux 中 3. 解压压缩文件 tar -zxvf redis 4. 安装gcc yum install -y gcc 5. 进入解压缩文件目录执行如下操作 make MAllOC=libc 6. 编译完成后执行如下命令 make install PREFIX=/usr/redis 7. 进入 /usr/redis/bin 目录启动 redis 服务 ./redis-server 成功后的界面

在同一台机器上操作 redis

不能退出上面的启动界面 另外一个窗口,执行

5、Redis 的操作细节

1. Redis 启动服务的细节 # 注意:直接使用 ./redis-server 方式启动使用的是 redis-server 这个 shell 脚本中默认配置 2. 如何启动 redis 中指定的配置文件启动 # 注意:默认redis安装完之后在安装的目录中没有任何配置文件,需要在源码目录中复制redis.conf配置文件到目录中 a. 进入源码目录 cp redis.conf /usr/redis b. 进入 /usr/redis 安装目录查看复制的配置文件 cd /usr/redis c. 进入 bin 目录加载配置启动 ./redis-server ../redis.conf 【此时启动就是配置文件中修改后的】 3. 修改 redis 默认的端口号 # vim redis.conf # 修改里面 port 7000 # 保存退出 4. redis 库的概念 # 库:database 用来存放数据的一个基本单元,一个库可以存放key-value键值对 # redis 中每一个库都有一个唯一名称(编号) ----》 从 0 开始(默认使用的库) # redis 中默认的库的个数:16 库的编号:0 ~ 15 5. 操作库的指令 # 切换库的命令: select dbid(库编号) # 清除库的命令: flushDB :清空当前库 flushAll:清空所有库

6、Redis 操作 key 的指令

1. 查找所有的 key 值 - keys * 2. DEL 指令 - 语法:DEL key [key...] - 作用:删除给定的一个或多个 key,不存在的 key 会忽略 - 返回值:被删除 key 的数量 3. EXISTS 指令 - 语法:EXISTS key - 作用:检查给定key 是否存在 - 返回值:若存在返回1,否则为0 4. Expire - 语法:Expire key seconds - 作用:为给定的 key 设置生存的时间,当 key 过期时,会自动删除 - 返回值:设置成功返回1 5. keys - 语法:keys pattern - 作用:查找所有符合给定模式 pattern 的 key【模糊查询】 - 具体语法: keys * 匹配数据库中所有的 key keys h?llo 匹配 hallo和hxllo等 【只能是一个字母】 keys h*llo 匹配 hllo 和heeello等 【可以是多个字母】 keys h[ae]llo 匹配hello和hallo 【可以是[]中的一个字母】 - 返回值:符合给定模式的 key 的列表 6. move - 语法:move key db - 作用:将当前数据库的 key 移动到给定的数据库 db 当中 - 返回值:移动成功返回1,失败则返回0. 7. pexpire - 语法:pexpire key millisecond - 作用:与Expire作用类似,但是是以毫秒为单位设置key的生存时间 - 返回值:设置成功,返回值为1 8. TTL - 语法:TTL key - 作用:以秒为单位,返回给定 key 的剩余生存的时间 - 返回值: key 不存在 返回-2 key 存在但没有设置剩余时间,返回-1 key 存在并设置了剩余时间,返回剩余的时间 9. PTTL - 语法:PTTL key - 作用:和 TTL 类似,不过是以 毫秒 为单位 10. Randomkey - 语法:Randkey - 作用:从数据库中随即返回一个 key - 返回值:当数据库不为空时,返回一个 key,为空时,返回 nil 11. Rename: - 语法:Rename key newkey - 作用:将 key 名改为 newkey - 返回值:成功为 OK 12. TYPE: - 语法:Type key - 作用:返回 key 所存储的值的类型 - 返回值: none(key 不存在) string(字符串) list(列表) set(集合) zset(有序集合) hash(哈希表)

7、String 类型

内存存储模型

常用操作命令

8、List 类型

内存存储模型(有序且 可重复)

常用操作指令

9、Set 类型

内存存储模型(无序且 不可重复)

常用命令

10、ZSet 类型

内存模型(可排序且不可重复,每个value都一个score)

常用命令

11、hash 类型

内存模型(value是一个map(key,value) 其中key是无序的)

常用命令

12、开启 Redis 的远程连接

13、Redis 的持久化机制

Redis 官方提供了两种不同的持久化方法将数据存储到硬盘中:

快照(Snapshot)AOF(Append Only File)只追加日志文件

13.1 快照

这种方式可以将某一时刻的所有的数据都写入硬盘中,这种方式也是 redis 的默认开启的持久化方式,保存的文件是以 .rdb形式结尾的文件,因此这种方式也称为 RDB 方式。

快照生成的方式:

客户端方式:BGSAVE 和 SAVE 指令,shutdown指令服务器配置自动触发 # 客户端方式之 BGSAVE: 客户端可以使用 BGSAVE 的命令来创建一个快照。当服务器接收到客户端的 BGSAVE 的命令时,redis 会调用 fork 来创建一个子进程,然后子进程负责将快照写到磁盘中,而父进程则继续处理命令的请求。 名词解释:【fork】当一个进程创建子进程的时候,底层操作系统会创建该进程的一个副本,在 类UNIX 系统中创建子线程的操作会进行优化:刚开始的时候,父子进程共享相同的内存,直到父进程或子进程对内存进行写操作之后,对被写入的内存的共享才会结束。

# 客户端方式之 SAVE: 客户端还可以使用 SAVE 命令来创建一个快照,接收到 SAVE 命令的 redis 服务器在快照创建完毕之前将不再响应任何其他的命令。 # 注意:SAVE 命令并不常用,使用 SAVE 命令在快照创建完毕之前,redis处于阻塞状态,无法对外服务。

# 服务器配置方式之满足配置自动触发: 如果用户在 redis.conf 中设置了 save 配置选项,redis会在save选项条件满足之后自动触发一次BGSAVE命令,如果设置多个save配置选项,当任何一个save配置选项条件满足,redis也会触发一次BGSAVE命令。

# 服务器接收到客户端 shutdown 指令 当redis通过shutdown指令接收到关闭服务器的请求时,会执行一个save指令,阻塞所有的客户端,不再执行客户端执行发送的任何命令,并且在save命令执行完毕之后关闭服务器。

配置生成快照的名称和位置

修改生成快照的名称 - dbfilename dump.rdb修改生成位置 - dir ./

13.2 AOF(只追加日志文件)

这种方式可以将所有的客户端执行的写命令记录到日志文件 中,AOF 持久化会将被执行的写命令写到AOF的文件末尾,以此来记录数据发生的变化,因此只要 redis 从头到尾执行一次 AOF 文件所包含的所有写命令,就可以恢复 AOF 文件的记录的数据集。

开启 AOF 持久化

在 redis 的默认设置中 AOF 持久化机制是没有开启的,需要在配置中开启 # 开启 AOF 持久化 1. 修改 appendonly yes 开启持久化 2. 修改 appendfilename "appendonly.aof" 指定生成文件名称

日志追加的频率设置

1. always 【谨慎使用】 每个 redis 写命令都要同步到硬盘,严重降低 redis 速度。 这种同步机制需要对硬盘进行大量的写入操作,所以 redis 处理命令的速度会受到硬盘性能的限制 不断写入少量的数据的做法可能会引发严重的写入放大问题,会大大降低硬盘的寿命。 2. everysec 【推荐】 每秒执行一次同步显式的将多个写命令同步到磁盘 redis 可以保证,即使系统崩溃,用户最多丢失一秒之内产生的数据 3. no 【不推荐】 由操作系统决定何时同步 这个选项不会对 redis 性能带来影响但是系统崩溃时,会丢失不定量的数据 另外,如果用户的硬盘处理写入操作不够快的话,当缓冲区被等待写入硬盘的数据填满时,redis 会处于 阻塞状态,并导致redis的处理命令请求的速度变慢。

13.3 AOF 文件的重写

AOF 带来的问题

> AOF 的方式也同时带来了另外一个问题,持久化的文件会变得越来越大。例如我们调用 incr num 命令1000 次,文件中必须保存全部的 1000 条命令,在实际恢复时,999条都是没有必要恢复的,其实我们的日志文件只需要保存一条 set num 1000 就可以了。 为了压缩 AOF 持久化文件,Redis 提供了 AOF重写(ReWrite)机制。 AOF 重写 在一定程度上减小了AOF文件的体积

触发 AOF 重写方式

1. 客户端方式触发重写 执行 BGREWRITEAOF 命令【不会阻塞redis服务】 2. 服务器配置方式自动触发 配置 redis.conf 中的auto-aof-rewrite-percentage 选项 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb 代表:当 AOF 文件体积大于64M时,并且AOF的体积比上一次重写之后的体积大了至少一倍(100%)时,会自动触发

重写原理

> 注意:重写 aof 文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,替换了原有文件。【类似于快照】 # 重写的流程: 1. redis 调用fork,有父子两进程,子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令 2. 父进程继续处理client的请求,一边会将命令写入原来的aof文件,同时,一边把收到的写命令缓存起来,只样做能够保证如果子进程重写失败的时候,数据不会丢失。 3. 当子进程把快照内容写到临时文件后,子进程会发出信号通知父进程,然后父进程会把缓存的写命令也写入到临时文件。 4. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始会往新的aof文件追加。

14、Java 操作 Redis

引入依赖 <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.3.0</version> </dependency> 创建 jedis 对象 public class Test { public static void main(String[] args) { //1、创建jedis对象 Jedis jedis = new Jedis("121.89.207.234",6379); //选择操作的库 jedis.select(0); //执行相关操作 String age = jedis.set("age", "23"); System.out.println(age); //释放资源 jedis.close(); } }

15、SpringBoot 整合 Redis

SpringBoot Data Redis 中提供了 RedisTemplate和 StringRedisTemplate .其中 StringRedisTemplate 是 RedisTemplate 的子类,两个方法基本一致,不同之处主要体现在:操作的数据类型不同,RedisTemplate

中的两个泛型都是 Object,意味着存储的 key 和 value 都可以是一个对象,而 StringRedisTemplate 的两个

泛型都是 String ,意味着存储 key 和 value 只能是字符串。

注意:使用 RedisTemplate 默认是将对象序列化到 Redis中,所以放入的对象必须实现对象序列化接口

引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 配置 application.properties spring.redis.host=121.89.207.234 spring.redis.port=6379 spring.redis.database=0 spring.redis.password=root 测试 //用于测试的坐标 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency>

15.1 StringRedisTemplate

操作 key //启动springboot应用 @SpringBootTest(classes = Springboot02Application.class) @RunWith(SpringRunner.class) public class TestApp { //注入 StringRedisTemplate【因为工厂中有,所以可以直接注入】 @Autowired private StringRedisTemplate stringRedisTemplate; @Test public void TestString(){ //操作redis中key //stringRedisTemplate.delete("name");//删除指定的 key Boolean hasKey = stringRedisTemplate.hasKey("name");//判断是否存在指定的 key System.out.println(hasKey); DataType type = stringRedisTemplate.type("name");//查看指定key对应值的类型 System.out.println(type); Set<String> keys = stringRedisTemplate.keys("*");//获取所有key for (String key : keys) { System.out.println(key); } } } 操作String类型 【opsForValue】 //操作redis中的字符串 @Test public void TestString(){ //操作redis中的字符串,opsForValue 实际操作的就是 redis 中的 String 类型 stringRedisTemplate.opsForValue().set("name", "张三"); //通过set可以设置超时事件 stringRedisTemplate.opsForValue().set("age","23",120,TimeUnit.SECONDS); String name = stringRedisTemplate.opsForValue().get("name"); System.out.println(name); } 操作List类型 【opsForList】 @Test public void TestString(){ //创建一个列表,并放入一个元素 stringRedisTemplate.opsForList().leftPush("names", "xaiohei"); //创建一个列表,并放入多个元素 stringRedisTemplate.opsForList().leftPushAll("names", "xiaoming","zhangsan","lisi"); //创建一个列表,并放入多个元素 List<String> names = new ArrayList<>(); names.add("wangwu"); names.add("tianqi"); stringRedisTemplate.opsForList().leftPushAll("names",names); //遍历元素 List<String> names1 = stringRedisTemplate.opsForList().range("names", 0, -1); names1.forEach(value-> System.out.println(value)); } 操作set类型【opsForSet】 @Test public void TestString(){ //创建set,并放入多个元素 stringRedisTemplate.opsForSet().add("sets", "zs","ls","ww","xh"); //遍历set成员 Set<String> sets = stringRedisTemplate.opsForSet().members("sets"); sets.forEach(value-> System.out.println(value)); } 操作zset类型【opsForZSet】 @Test public void TestString(){ //创建zset stringRedisTemplate.opsForZSet().add("zsets","zs",100); stringRedisTemplate.opsForZSet().add("zsets","ls",20); stringRedisTemplate.opsForZSet().add("zsets","wu",50); //遍历zset成员 Set<String> zsets = stringRedisTemplate.opsForZSet().range("zsets", 0, -1); zsets.forEach(value-> System.out.println(value)); } 操作Hsah类型【opsForHash】 @Test public void TestString(){ //创建Hash stringRedisTemplate.opsForHash().put("maps", "name", "zss"); //创建Hash放入Map Map<String,String> map = new HashMap<>(); map.put("age", "23"); map.put("bir", "2020-1-1"); stringRedisTemplate.opsForHash().putAll("maps",map); //获取hash中的某个key的value值 Object o = stringRedisTemplate.opsForHash().get("maps", "name"); System.out.println(o); //获取所有的values List<Object> values = stringRedisTemplate.opsForHash().values("maps"); values.forEach(value-> System.out.println(value)); //获取所有的key Set<Object> keys = stringRedisTemplate.opsForHash().keys("maps"); keys.forEach(value-> System.out.println(value)); }

15.2 RedisTemplate

//启动springboot应用 @SpringBootTest(classes = Springboot02Application.class) @RunWith(SpringRunner.class) public class TestApp { //注入RedisTemplate【因为工厂中有,所以可以直接注入】 //key<Object> value<Object> 因为Redis中的key只能存放字符串类型,所以存储前,先要将key中的Object序列化 @Autowired private RedisTemplate redisTemplate; //操作redis中key相关 @Test public void TestString(){ User user = new User(); user.setName("zs"); user.setId(1); redisTemplate.opsForValue().set("user", user); User user1 = (User) redisTemplate.opsForValue().get("user"); System.out.println(user1); } }

因为用RedisTemplate,key和value都是Object类型,所以在存储到Redis中时,会先将key和value序列化,再存储到Redis中,我们可以在Redis中查看上面存储的key在Redis中的显示

并且在Redis查询时,也无法查询

get “\xac\xed\x00\x05t\x00\x04user”

此种方法存在局限性:在客户端我们是无法查询(使用)这个key所对应的value了。

为了解决这个问题,我们必须将 RedisTemplate中的key 的JDK序列化方式改为String序列化的方式

redisTemplate.setKeySerializer(new StringRedisSerializer());

public void TestString(){ //修改 key 的序列化方案为 String序列化 redisTemplate.setKeySerializer(new StringRedisSerializer()); User user = new User(); user.setName("zs"); user.setId(1); redisTemplate.opsForValue().set("user", user); User user1 = (User) redisTemplate.opsForValue().get("user"); System.out.println(user1); }

此时,Redis中的key就是user

并且可以通过key来查询

hash类型的key的序列化:redisTemplate.setHashKeySerializer(new StringRedisSerializer());

16、Redis 应用场景

利用redis中的字符串类型完成项目中的手机验证码存储的实现。

利用redis中的字符串类型完成项目中的订单】失效性的业务。

利用redis 实现分布式集群系统中Session共享

利用redis中的ZSet类型,排行榜之类的功能

利用redis 实现分布式缓存

利用redis 存储认证之后token 信息

利用redis 解决分布式集群系统中分布式锁的问题

16.1、分布式缓存

1.认识分布式缓存

什么是缓存?

就是计算机内存中的一段数据

内存中数据有什么特点?

读写快断电立即消失

缓存解决了什么问题?

提高了网站的吞吐量、提高了网站的运行效率核心解决的问题: 缓存的存在是用来减轻数据库的压力

什么地方需要使用缓存?

数据库中极少发生修改,更多用于查询的数据需要使用缓存。

本地缓存和分布式缓存 的区别?

1. 本地缓存:存在于应用服务器内存中的数据称之为本地缓存 例如:MyBatis中的缓存,是存在于Tomcat的内存中的,当Tomcat关闭,缓存的数据也就消失了【占用JVM内存】 2. 分布式缓存:存储在当前应用服务器之外的内存中数据称之为分布式缓存 例如:在 redis 中的数据,当Tomcat关闭时,缓存的数据不会消失,而是存在于redis中。

集群 和 分布式

1. 集群:将同一种服务的多个节点放在一起共同对系统提供服务 例如:Tomcat集群、MySQL集群、Redis集群... 2. 分布式:有多个不同的服务集群共同对系统提供服务这个系统称之为分布式系统 例如:Tomcat集群、MySQL集群、Redis集群共同为一个系统提供服务
2.分析本地缓存

利用 MyBatis 的本地缓存来看 Redis 如何实现分布式缓存

1. MyBatis中的应用级缓存(二级缓存) SqlSessionFactory级别的缓存是所有会话共享的。 2. 开启二级缓存 在 XxxMapper.xml 中加入 <cache/> <mapper namespace="cf.duanzifan.dao.UserDao"> <!--开启二级缓存--> <cache/> <select id="findAll" resultType="User"> select * from t_user; </select> </mapper> 使用缓存,实体类必须实现序列化 public class User implements Serializable { 第二次查询时:

本地缓存的缺点:

​ 1.本地缓存关闭应用服务器就会断电消失,并且占用JVM的内存

​ 2.在启用Tomcat集群时,Nginx会进行负载均衡,此时如果还是用的本地缓存,在负载均衡时会重复建立缓存

此时我们需要分布式缓存解决问题

我们先对本地缓存的 < cache/ > 进行分析:

MyBatis中的 Cache 是由 PerpetualCache 实现的

我们可以看一下 PerpetualCache 的源码

public class PerpetualCache implements Cache { private final String id; //此处可以看出底层实现缓存是:HashMap private Map<Object, Object> cache = new HashMap(); public PerpetualCache(String id) { this.id = id; } public String getId() { return this.id; } public int getSize() { return this.cache.size(); } //我们将这里两个方法修改为 Redis 的实现 public void putObject(Object key, Object value) { this.cache.put(key, value); } //我们将这里两个方法修改为 Redis 的实现 public Object getObject(Object key) { return this.cache.get(key); } public Object removeObject(Object key) { return this.cache.remove(key); } public void clear() { this.cache.clear(); } public boolean equals(Object o) { if (this.getId() == null) { throw new CacheException("Cache instances require an ID."); } else if (this == o) { return true; } else if (!(o instanceof Cache)) { return false; } else { Cache otherCache = (Cache)o; return this.getId().equals(otherCache.getId()); } } public int hashCode() { if (this.getId() == null) { throw new CacheException("Cache instances require an ID."); } else { return this.getId().hashCode(); } } }

我们可以看出

<cache/> ========》 <cache type="org.apache.ibatis.cache.impl.PerpetualCache"/> 结论 # MyBatis 的缓存底层默认使用的是 org.apache.ibatis.cache.impl.PerpetualCache 来实现的 # 我们可以自定义 RedisCache 来实现 Redis 的分布式缓存 1.我们可以自定Cache类来实现Cache接口,对接口中的方法进行实现 2.我们以后在使用分布式缓存时就可以 <mapper namespace="cf.duanzifan.dao.UserDao"> <!--开启Redis缓存--> <cache type:"xxx.RedisCache"/> <select id="findAll" resultType="User"> select * from t_user; </select> </mapper>
3.实现 Redis 分布式缓存

在设计分布式缓存时我们应该采用什么样的Redis类型

由此我们可以看出采用RedisHash类型

//用来获取springboot创建好的工厂,保证在 RedisCache 缓存类中可以使用 RedisTemplate @Component public class ApplicationContextUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; //将创建好的工厂以参数的形式传递给这个类 @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { //将参数中的工厂赋给定义好的工厂 this.applicationContext = applicationContext; } //提供一个静态方法通过工厂获取对象 //RedisTemplate redisTemplate public static Object getBean(String beanName){ return applicationContext.getBean(beanName); } } public class RedisCache implements Cache { //当前放入缓存的mapper的namespace private final String id; //自定义Cache实现类,必须存在一个带有id参数的构造方法 public RedisCache(String id) { this.id = id; } //此处必须返回Cache的唯一标识 @Override public String getId() { return this.id; } //封装redisTemplate private RedisTemplate getRedisTemplate(){ //通过application工具类获取 RedisTemplate RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); return redisTemplate; } //缓存中放值 用 RedisTemplate @Override public void putObject(Object key, Object value) { //使用redishash类型作为缓存存储模型 key hashkey value getRedisTemplate().opsForHash().put(id.toString(), key.toString(), value); } //缓存中取值 @Override public Object getObject(Object key) { return getRedisTemplate().opsForHash().get(id.toString(), key.toString()); } //根据指定的key删除缓存(保留方法,默认无实现) @Override public Object removeObject(Object key) { return null; } //清空缓存(增删改的时候,默认走的就是清空缓存) @Override public void clear() { //清空namespace getRedisTemplate().delete(id.toString());//清空缓存 } //用来计算缓存数量 @Override public int getSize() { //获取hash中的key、value数量 return getRedisTemplate().opsForHash().size(id.toString()).intValue(); } @Override public ReadWriteLock getReadWriteLock() { return null; } } <mapper namespace="cf.duanzifan.dao.UserDao"> <!--开启Redis分布式缓存--> <cache type="cf.duanzifan.cache.RedisCache"/> <select id="findAll" resultType="User"> select * from t_user; </select> </mapper>

4.redis 分布式缓存的多表联合

上面的设计有什么缺陷吗?

显然此种方法在单表查询时,没有任何问题但是,如果存在联合查询,则上面存在一定的问题: 例如我们现在有一张用户表和一张员工表,当用户表更新时,员工表的缓存不会清空,会存着之前旧的值,再次查询时必然会出现问题我们如何来解决:当两张表有关联查询时,我们要保证两张表的缓存存在Redis的同一个HsahKey中<cache-ref namespace="cf.duanzifan.dao.UserDao"/>

单表查询时

多表联合时

5.redis 分布式缓存的优化策略

我们如何更进一步的去优化此种分布式缓存?

# 我们发现在放入hash中的key,默认生成的比较长,这样能加消耗内存 1. key:-35986374:1262414444:cf.duanzifan.dao.UserDao.findAll:0:2147483647:select * from t_user;:SqlSessionFactoryBean # 所以,我们有了缓存的优化策略 1. 对放入 redis 的 key 进行优化,key的长度不能太长 我们可以采用 MD5 算法【加密算法】对 key 进行处理 MD5算法的特点: 1. 一切文件字符串等经过md5处理之后,都会生成32为16进制的字符串 2. 不同内容经过md5进行加密,加密结果一定不一致 3. 相同内容的文件多次经过md5生成的结果始终一致 所以,我们可以在redis整合mybatis的过程中,可以将key进行md5优化处理

我们在 ResdisCache 中加一个方法

//封装一个对key进行md5处理的方法 private String getKeyToMD5(String key){ return DigestUtils.md5DigestAsHex(key.getBytes()); } //在放值和取值的方法中将key进行md5处理 @Override public void putObject(Object key, Object value) { //使用redishash类型作为缓存存储模型 key hashkey value getRedisTemplate().opsForHash().put(id.toString(), getKeyToMD5(key.toString()), value); } @Override public Object getObject(Object key) { return getRedisTemplate().opsForHash().get(id.toString(), getKeyToMD5(key.toString())); }

此时key值为

79773edac7a7a41d58076c76af068c9a

6、缓存穿透和缓存雪崩

什么是缓存穿透?

客户端查询了一个数据库中没有的数据导致缓存无法利用

注意:mybatis中的cache解决了缓存穿透:将数据库中没有的数据的查询结果也进行缓存

什么是缓存雪崩?

在系统运行的某一时刻,突然系统中缓存全部失效,恰好在这一时刻涌来大量的用户请求,导致所有的模块缓存无法利用,导致数据库阻塞或挂起(一般实战中缓存是有时效性的,如果缓存在同一时间设置了相同的时效,就会发生雪崩)

解决方案:

1.缓存永久存储 不推荐2.不同业务的数据设置不同的超时时间

17、Redis 主从复制

​ 主从复制架构是用来解决数据的冗余备份,从节点仅仅是用来同步数据的。

无法解决的问题:

自动故障转移

单节点并发压力问题

单节点内存和磁盘物理上限

主从复制架构图

搭建主从复制

# 1.准备三台机器并修改配置 - master port 6379 bind 0.0.0.0 - slave1 port 6380 bind 0.0.0.0 slaveof xxx.xxx.xx.x(asterip) 6379(masterport) - slave1 port 6381 bind 0.0.0.0 slaveof xxx.xxx.xx.x(asterip) 6379(masterport)

18、Redis 哨兵机制

Sentinel(哨兵) 是 Redis 的高可用解决方案:由一个或者多个 Sentinel 实例组成的 Sentinel 系统,可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线的主服务器的某个从服务器升级为新的主服务器,当原先的主服务器恢复时,会成为新的主服务器的从节点

简单说:哨兵就是带有自动故障转移功能的主从架构

无法解决的问题:

单节点并发压力问题单节点内存和磁盘物理上限

搭建哨兵架构

19、springboot操作哨兵集群

在哨兵的配置文件中开启远程访问:bind 0.0.0.0

在springboot的配置文件中加入配置 # redis sentinel 配置 spring.redis.sentinel.master=被监控数据库(主从架构)的名字 spring.redis.sentinel.nodes=哨兵的节点,可以有多个,用逗号隔开

20、Redis 集群

集群搭建

21、Redis 实现分布式 Session 管理

进行 MSM 和 RSM 的比较:

​ 是应用服务器管理 Session,在服务器上的所有应用都会session共享

​ 当用到 session 的时候会在 Memcache 中复制到应用服务器中使用

是具体的应用管理 Session,加上session共享的应用才会session共享

​ 当用到 session 的时候会在 Redis 中直接获取session

# redis的session管理是利用spring提供的session管理解决方案,将一个应用session交给Redis存储,整个应用中所有的session的请求都会去redis中获取对应的session数据。

开发 session 管理

引入依赖 <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> 开发 session 管理配置类 @Configuration @EnableRedisHttpSession public class RedisSessionManager { } 打包测试即可

127052163)]

[外链图片转存中…(img-hWj0PXQ8-1599127052166)]

[外链图片转存中…(img-37NMTkLK-1599127052168)]

[外链图片转存中…(img-uD5lSycK-1599127052170)]

[外链图片转存中…(img-TTnYb7g3-1599127052172)]

[外链图片转存中…(img-TbS8is7e-1599127052174)]

[外链图片转存中…(img-CaR12vaN-1599127052177)]

[外链图片转存中…(img-31jRAZ63-1599127052180)]

21、Redis 实现分布式 Session 管理

进行 MSM 和 RSM 的比较:

[外链图片转存中…(img-UKdlc4o8-1599127052182)]

​ 是应用服务器管理 Session,在服务器上的所有应用都会session共享

​ 当用到 session 的时候会在 Memcache 中复制到应用服务器中使用

[外链图片转存中…(img-KfOhXsWE-1599127052185)]

是具体的应用管理 Session,加上session共享的应用才会session共享

​ 当用到 session 的时候会在 Redis 中直接获取session

# redis的session管理是利用spring提供的session管理解决方案,将一个应用session交给Redis存储,整个应用中所有的session的请求都会去redis中获取对应的session数据。

开发 session 管理

引入依赖 <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> 开发 session 管理配置类 @Configuration @EnableRedisHttpSession public class RedisSessionManager { } 打包测试即可
最新回复(0)