牛客后端项目(5)-整合Redis与相关功能

使用Redis保存高频访问的数据,提高读写性能

Redis的简介

NoSql数据库,用键值对来保存数据。可以保存的数据类型一共有5种,分别是:字符串、列表、哈希、集合、有序集合。存放在内存中,以实现高速读写;同时也有缓存功能,使数据持久化。

注意是没有数字类型的,统一用字符串来保存

为什么这些功能要用Redis?

  1. 访问频率高;2. 失效管理;3. 分布式问题;

更多介绍:
图解Redis介绍 | 小林coding
Redis 教程 | 菜鸟教程

Spring整合Redis

引入依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

由于Spring在对Redis进行自动配置的时候,对于默认模板(见RedisAutoConfiguration类源码)得到的是RedisTemplate<Object, Object>类型。Object的范围太大了,虽然更通用,但如果能他对类型能更准确的话,对使用者来说会更便利。

点赞功能

基本功能👍

点赞模块的基本功能:点击一次进行点赞,第二次就是取消点赞了;统计点赞数量;查询用户是否对实体进行点赞。

为了功能具有可扩展性,需要注意以下几点:

  1. 点赞功能不止对帖子,还有评论等,通过存储时key包含实体类型
  2. 点赞状态用整型保存,而不是布尔值,为了之后扩展功能(如,点踩)

和之前模块开发类似,只是把对mysql的CRUD改成了对redis的CRUD~

我收到的赞👍

增加一个功能就行:统计用户收到的赞,另外在redis中保存,key为userID,value为获得赞的数量。
另外需要从前端传回点赞的目标用户是谁。

关注

和点赞功能类似,不过点赞只统计我收获的赞,没有统计我得出的赞,是单向的;关注是双向的,也就是说既要统计我关注的,也要统计关注我的,另外需要保存具体用户id,而不只是一个关注数量。

注意⚠️
关注的对象可以是用户,也可以是别的(帖子等),为了可以扩展和复用,抽象为实体。在保存关注列表和被关注列表时,关键字中加入实体类型

优化

Img

验证码的优化

之前验证码保存在session中,这样会带来两个问题:

  1. session在分布式系统中存在一致性问题
  2. 验证码不需要长期保存,只需要保存一段时间,即不需要数据持久化
  3. 验证码在登录时可能会多次刷新,使用频率也比较高

综上所述,我们用Redis来保存验证码,并设置有效时长。

具体步骤

  1. 设计保存的K-V对,value是验证码,key需要有一个标识,又因为此时并没有登录用户,所以也不能用用户id,但是我们可以在用户请求获得验证码的时候,返回一个凭证保存在Cookie中来表示该用户,令这个凭证为ownerId(实现时可以用UUID来获得一个随机字符串):community:kaptcha:ownerId
  2. 在请求验证码图像的时候保存验证码,并向客户端的响应中放一个Cookie存放凭证
  3. 登录时从cookie中取凭证,在redis中找验证码

登录凭证的优化

在之前的开发中,登录凭证保存在Mysql中。每次访问一个页面都要查询一次登录状态,因此请求频繁,而每次向Mysql查询会存在以下问题:

  1. 请求频繁,数据库访问压力大,可能导致速度变慢

综上,把登录凭证存一份在Redis中,

为什么不全部存在Redis中,过期删除?
用户的登录情况应当保存下来,因为可能需要统计用户的登录情况等,是有效数据。

User的优化

对user数据的读取也很频繁,因此加入缓存管理。对UserService进行优化,加入缓存。
缓存管理有三部分:

  1. 优先从缓存中取值
  2. 当缓存中没有值时,初始化缓存
  3. 当需要更新值时,删除缓存

注意给缓存设置有效时间

其他问题

缓存击穿、缓存雪崩和缓存穿透

Redis性能高有一个重要原因是它把数据存在缓存中,保证了数据的高速读取。然而放在缓存中的数据会带来这三个问题:缓存击穿、缓存雪崩和缓存穿透。

三个概念

缓存击穿:当Redis中有一个Key是热点,有很多请求同时访问这个Key,而如果这个Key在某个时间突然失效了,那么这些请求都会同时去访问数据库。

缓存穿透:有这样一些Key,在数据库和缓存中都是不存在的,如果用户大量访问这些Key,那么大量请求会直接穿透缓存,访问数据库。

缓存雪崩:有这样大量的一堆Key,他们在同一时间或者很短的一个时间间隔中一起失效了,这个时候用户来访问这些Key,请求就都传到数据库,压力就给到了数据库这边。

怎么解决?

总的来说,这些情况都是压力给到了数据库所导致的问题。针对每个问题的解决措施:

缓存穿透:这个问题和另外两个不一样,这里请求的数据在缓存和数据库中都没有。解决措施:1. 拦截器/过滤器,拦截无效请求。2. 布隆过滤器,在这里保存了可能存在的Key值,在查缓存之前先经过布隆过滤器。3. 在缓存中保存这些Key,值为null

缓存击穿:1. 热点数据可以不设置失效时间,保证一直可以访问;2. 用互斥锁,只让一个请求通过。

缓存雪崩:1. 提高数据库的抗灾能力,如分库分表;2. 提高Redis的抗灾能力,如建立Redis集群;3. 使用熔断机制,当流量过大时,返回“系统繁忙”;4. 在设置失效时间的时候,随机一点