- 简介Redis
- Redis安装
- 怎么远程连接
- 基础命令
- 常用类型
- 字符串类型
- 字典类型
- 列表类型
- 集合类型
- 有序集合类型
- spring boot集成Redis
- 代码操作Redis
- 实现一个session储存到redis
- 持久化
- 缓存雪崩
- 缓存穿透
- 缓存击穿
- 缓存预热
- 项目升级之Redis储存Session
简介Redis
Redis是一种内存数据存储系统,也被称为键值存储系统。之所以能这么快;因为他的数据是储存在内存中;在内存就意味着可能会因为断电关机而丢失;但是我们的云服务器是7*24小时工作的;而Redis也是有持久化机制;把数据写入硬盘里(RDB、AOF等)
Redis支持多种数据结构,如字符串、哈希、列表、集合和有序集合。它可以存储非结构化数据(文本、图片、音频),适用于缓存、队列等场景。
如果session存在内存:只能单机部署;存在Redis到时候大家都连接这个redis;实现分布式。(假设我觉得一台服务器处理100w客户端压力很大;所以我选择两台服务器;用Redis就不会说有台服务器是没有Session的情况)
Redis安装
官方的redis只提供了linux版本。windows装的那都是绿色版本;我们在Xshell上所以:yum -y install redis
Redis启动:以后台方式运行
redis-server /etc/redis.conf& (默认是在etc路径)
启动redis客户端:redis-cli (这里注意不能带空格)
怎么远程连接
1.将 redis 配置文件下载到本地: redis 配置文件是 linux 下的 /etc/redis.conf ;
找到你要下载的文件;路径+文件名。通过sz /路径+文件名;然后会谈出一个框让你选择下载到哪里
2.将 redis.conf 中的"bind 127.0.0.1”注释掉
3.将 redis.conf 中的“protected-mode yes”改为“protected-mode no"
4.将修改后的 redis.conf 上传至 liunx 下的 /etc 目录;(将文件拖进去就能上传;亲测:得把原先的redis.conf配置文件删除掉再上传;记得云服务器端口开放一下)
5.使用命令“redis-cli shutdown” 先关 redis 服务,再使用“redis-server /etc/redis.conf &"启动 redis 服务。
安装Redis可视化客户端:如有需要的伙伴私我发你;我会在第一时间分享与你;Redis没有用户名;6.0以后才有用户名
基础命令
操作命令:官方文档
多问问chatgpt;一般我们都是通过代码来操作的比较多;
常用类型
字符串类型
key-value:
String类型也能相加
设置过期时间:
使用场景:存用户登录信息、文章信息、列表信息、累计网页统计信息。string可以存万物;哪怕是对象也能按json存;只是要转来转去
字典类型
字典类型(Hash) 又被成为散列类型或者是哈希表类型,它是将一个键值(key) 和一个特殊的“哈希表”关联起来,这个“哈希表”表包含两列数据: 字段和值,它就相当于 Java 中的Map<String,Map<String,String>> 结构。套娃是吧!
使用:
设置多个:
但是这个设置多个是有问题的;这一个起始的版本是4.0。我们装的是3.多;所以这个是用不了的
使用HMSET设置多个时:
使用场景:用户对象;把用户id、用户名、创建时间都传进来;
列表类型
列表类型(list)是一个使用链表结构存储的有序结构,它的元素插入会按照先后顺序存储到链表结构中,因此它的元素操作(插入和删除)时间复杂度为 O(1),所以相对来说速度还是比较快的,但它的查询时间复杂度为 O(n).因此查询可能会比较慢
使用场景:
消息队列: 列表类型可以使用 push 实现先进先出的功能,同时又可以使用 pop 轻松的弹出(查询并删除)第一个元素,所以列表类型可以用来实现简单消息队列;
文章列表:对于博客站点来说,当用户和文章都越来越多时,为了加快程序的响应速度,我们可以把用户自己的文章存入到 List 中,因为 List 是有序的结构,所以这样又可以完美的实现分页功能,从而加速了程序的响应速度。
集合类型
list是先进先出;可以重复;set不可以重复;如果存的重复就合并数据;
使用场景:去重复
微博关注我的人和我关注的人都适合用集合存储,可以保证人员不会重复;
中奖人信息也适合用集合类型存储,这样可以保证一个人不会重复中奖
集合类型 (Set) 和列表类型 (List) 的区别如下:
列表可以存储重复元素,集合只能存储非重复元素
列表是按照元素的先后顺序存储元素的,而集合则是无序方式存储元素的。
有序集合类型
能保证顺序;能根据用户设置的值进行排序:
有序集合类型(Sorted Set) 相比于集合类型多了一个排序属性 score (分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。有序集合的存储元素值也是不能重复的,但分值是可以重复的。
使用场景:学生成绩排名;粉丝列表;根据关注的先后时间排序
spring boot集成Redis
上述类型;字符串类型最常用;我们需要通过spring boot项目操作这些数据类型;连接redis
1:添加依赖
2:添加配置
如果你有设置密码;那这里就要写密码;key=value;默认的database有16个;而我们选择默认的是0。edis不能像MySQL自己建数据库。
如果超过16个;那就再搭建用一个redis服务器。
spring data redis底层基于lettuce设置的;所以我们这里可以设置lettuce的信息;相当于你设置redis。。后面这些可以选择性设置
默认是在内存;但是可以在磁盘;;你可以认为有两份数据;一份在内存;一份在硬盘。。内存的是最新的;磁盘里的会隔一段时间更新
最基本设置项:
代码操作Redis
注意:如果是专业版idea;@Autowired会提示报错;但是不影响效果
RedisTemplate是Spring Framework中的一个类,用于在Java应用程序中与Redis数据库进行交互的工具。它提供了一种方便的方式来执行常见的Redis操作,如存储、检索和删除数据,以及执行一些高级操作如事务和管道。通过RedisTemplate,您可以在Java代码中操作Redis数据库,而无需直接处理底层的Redis命令。这对于在Spring应用程序中使用Redis作为数据存储和缓存非常有用。
先获取操作的opsFor……;获取操作的类型。字符串类型就是value。很多重载的方法;你想操作什么类型就获取什么类型
注意:默认是没有设置过期时间;负1
具体代码:
@RestController
public class RedisController {@Resourceprivate RedisTemplate redisTemplate;@RequestMapping("/save")public String save() {redisTemplate.opsForValue().set("userinfo", "zhangsan");return "ok";}@RequestMapping("/get")public Object get() {return redisTemplate.opsForValue().get("userinfo");}//操作哈希@RequestMapping("/save2")public String save2() {redisTemplate.opsForHash().put("myhash", "username", "lisi");return "ok";}@RequestMapping("/get2")public Object get2() {return redisTemplate.opsForHash().get("myhash", "username"); // lish}}
执行:我们现在去浏览器输入这个url;触发这些执行
效果:发现Redis确实有点东西
实现一个session储存到redis
需要添加多这个依赖:
配置信息;这些都建议设置一下;namespace相当于新建文件夹储存。
spring.session.store-type=redis:指定将会话(Session)数据存储在Redis数据库中
spring.session.redis.flush-mode=on_save :意味着您选择了默认的刷新模式,即在会话数据发生变化时将其刷新到Redis中。
server.servlet.session.timeout :设置会话(Session)的超时时间的
spring.session.redis.namespace: 用来指定会话存储在Redis数据库中的命名空间(namespace);相当于新建文件夹储存。
存Session:还是之前的方式;不需要去改;httpSession还是白送的;只需要添加依赖和配置就行了。这就存进去了。存的时候会自动调用这个框架;把你的session存到redis里。
存字符串:
取字符串:
session.getAttribute(SESSION_KEY_USERINFO);
存对象:
存字符串不是关键;我们要存对象比较关键;比较对象的作用范围广很多;但是有坑
现在这是一个对象:使用上述的方法去存
序列化的时候报错;当然是没有匹配的对象啊。实现了这个接口才能序列化(将对象转换为字节流)和反序列化(将字节流转换回对象)。这样子的话;他储存会使用哈希map储存。
为什么要这样子呢?
1:数据持久化;内存数据库的数据是易失性的,意味着如果服务器重启或断电,所有数据将丢失。为了保证数据的持久性,可以将数据存储在硬盘上的持久化文件中。在这种情况下,需要将数据序列化为字节流,以便将其写入持久化文件,然后在重新加载时将其反序列化为对象。
2:网络传输;Redis是一个多语言支持的数据库,它可以被多种编程语言访问和操作。不同的编程语言使用不同的对象表示方法,通过将对象序列化为通用的字节流,可以跨语言地存储和检索数据。
3:存储优化;当对象需要在不同的机器或不同的进程之间进行传输时,需要将对象转换为字节流以进行网络传输。字节流可以在网络上进行传输,并在目标机器或进程上反序列化为对象。这在分布式系统中非常常见,以便不同部分之间共享数据。
4:支持多种编程语言;Redis是一个键值存储数据库,存储的是键和值对。值可以是字符串、哈希、列表等数据结构,但它们都需要被转换为字节流来存储在Redis中。序列化使得存储更加紧凑,节省了内存和存储空间。
如果不想序列化;加这个东西把它排除掉;就没法存进去的。现在你再设置这个age;他也不会读到这个;序列化把这个忽略了。
持久化
持久化:将数据保存在硬盘;而不是一直在内存;防止数据的丢失
Redis持久化方式:
RDB:快照方式;将某⼀个时刻的内存数据,以⼆进制的⽅式写⼊磁盘;
AOF:文件追加方式;记录所有的操作命令,并以⽂本的形式追加到⽂件中;
RDB、AOF结合:混合持久化方式;Redis 4.0 之后新增的⽅式,混合持久化是结合了 RDB 和 AOF 的优点,在写⼊
的时候,先把当前的数据以 RDB 的形式写⼊⽂件的开头,再将后续的操作命令以 AOF 的格式存⼊⽂件,这样既能保证 Redis 重启时的速度,⼜能减低数据丢失的⻛险。
如何切换呢?
redis-cli 命令⾏中执⾏ config set aof-use-rdb-preamble yes 来开启混合持久化,当开启混合持久化时 Redis 就以混合持久化⽅式来作为持久化策略;当没有开启混合持久化的情况下,使⽤ config set appendonly yes 来开启 AOF 持久化的策略,当 AOF 和混合持久化都没开启的情况下默认会是 RDB 持久化的⽅式。
优缺点:
缓存雪崩
缓存雪崩是指在短时间内,有⼤量缓存同时过期,导致⼤量的请求直接查询数据库,从⽽对数据库造成了巨⼤的压⼒,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。
如果都不设置过期时间行不行;永久的;不会过期。这样子持久化的时候压力就会很大;刚开始RGB;刚开始有用信息可能存个10mb;全部都不过期;那么存个100mb。
解决方案:所以加锁排队执行(增加系统响应时间会牺牲一些用户体验);或者随机过期时间;再或者设置二级缓存;Redis失效就先去查二级缓存而不是直接查数据库
设置随机过期时间
// 缓存原本的失效时间
int exTime = 10 * 60;
// 随机数⽣成类
Random random = new Random();
// 缓存设置
jedis.setex(cacheKey, exTime+random.nextInt(1000) , value);
缓存穿透
缓存穿透是指查询数据库和缓存都⽆数据,因为数据库查询⽆数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透。也会给数据库造成很大压力。
就是你叫给我托管;但是你啥都没有。用户向我拿东西;发现什么都没有;就一直找数据库拿。数据库也是什么都没有。
解决方案:
空值缓存:当一个请求查询的结果在数据库中不存在时,可以将这个空结果也缓存起来,设置一个较短的过期时间。我们在缓存中存储一个表示空结果的占位值,并为这个占位值设置一个较短的过期时间,当请求到达时,即使缓存中没有有效的数据,也会返回这个空结果。由于过期时间较短,这个占位值会很快从缓存中移除。
缓存击穿
缓存击穿指的是某个热点缓存,在某⼀时刻恰好失效了,然后此时刚好有⼤量的并发请求,此时这些请求将会给数据库造成巨⼤的压⼒,这种情况就叫做缓存击穿。
解决方案:
加锁排队:
此处理⽅式和缓存雪崩加锁排队的⽅法类似,都是在查询数据库时加锁排队,缓冲操作请求以此来减少服务器的运⾏压⼒。
设置永不过期:
把热点的设置不过期;这样子就不会去请求数据库,但需要注意在数据更改之后,要及时更新此热点缓存,不然就会造成查询结果的误差。
缓存预热
系统启动的时候,先把查询结果预存到缓存中,以便⽤户后⾯查询时可以直接从缓存中读取,以节约⽤户的等待时间。
应用程序启动就先查询数据库然后同步到缓存中:
实现的三种思路:
1: 把需要缓存的⽅法写在系统初始化的⽅法中,这样系统在启动的时候就会⾃动的加载数据并缓存数据
2:把需要缓存的⽅法挂载到某个⻚⾯或后端接⼝上,⼿动触发缓存预热;
3:设置定时任务,定时⾃动进⾏缓存预热。