仿牛客网项目第四章:Redis,一站式高性能存储方案(详细步骤和思路)

1. Redis入门

1.1 基本特性

  1. Redis是一款基于键值对NoSQL数据库
  2. 支持多种数据结构:字符串(strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。
  3. Redis将所有的数据都存放在内存中,所以它的读写性能十分惊人。
  4. Redis还可以将内存中的数据以快照日志的形式保存到硬盘上,以保证数据的安全性。(快照性能好,日志实时性高)
  5. Redis典型的应用场景包括:缓存、排行榜、计数器、社交网络、消息队列等(只要是频繁查询局部信息都可以用)

1.2 数据结构的使用

//1)开启redis
redis-cli
//2)选择redis库,有好几个
select 1;
select 2;
//3)清除分库的内容
flushdb
//4)查询库所有内容
keys *
//4)数据类型一:字符串(经常用户点赞数的统计)
set test:count 1     //给变量count赋值为"1"
set test:string abc  //给变量string赋值为abc
get test:count       //获取变量count的值
incr test:count      //对变量自增1
decr test:count      //对变量自减1
//5)数据类型二:哈希(map)
hset test:user username zhangsan  //为哈希表的usename赋值zhagnsna
hset test:user id 1               //为哈希表的id赋值1
hget test:user id			      //查询		
hget test:user username                //查询
//6)数据类型三:双向列表(list)(可以从前插入和从后插入,查询前面和后面)
lpush test:ids 101 102 103  //从左边依次插入三个
llen test:ids //查询数组长度
lindex test:ids 0  //查询数组索引为0的值,103
lindex test:ids 2  //查询数组索引为2的值,101
lrange test:ids 0 2  //批量范围查询
rpop test:ids    //从右边开始删除弹出值,101
rpop test:ids    //从右边开始删除弹出值,102
//7)数据类型四:集合sets(无序)
sadd test:teachers aaa bbb ccc ddd  //存入数据
scard test:teachers  //查询集合的大小
spop test:teachers   //随机弹出一个集合(**可以用于抽奖**)
//8)数据类型五:有序集合(sorted sets,带一个热度值排序,故常用于热度排行)
zadd test:students aaa 100 bbb 30 ccc  20 ddd 50 eee 10  fff 20 
zcard test:students  //查询有序集合的大小
zrank test:students ccc   //查询ccc的排序为多少,从小到大排序
zrange test:students 0 2   //查询热度为0到2的对象是谁

2. Spring整合Redis

  1. 步骤一:引入依赖 spring-boot-starter-data-redis
  2. 步骤二:配置Redis
    1)配置数据库参数(库,域名和端口)
    2)编写配置类,构造Redis Template(建立连接Redis的工厂,还有序列化Value,Key,Hash的Key和Value)
    3)后面就可以 通过Redis Template进行Redis的访问
  3. 步骤三:访问Redis
    1)访问字符串:redisTemplate.opsForValue()
    2)访问哈希:redisTemplate.opsForHash()
    3)访问双向链表:redisTemplate.opsForList()
    4)访问无序集合:redisTemplate.opsForSet()
    5)访问有序集合:redisTemplate.opsForZSet()
  4. 效果展示:在测试类
    1)测试类,对字符串,双向链表,哈希,无序集合和有序集合
    2)如果每次都是访问一个对象,可以进行绑定,这样就不用每次传入对象进去。
    3)Redis不是关系型数据库(Mysql),不完全符合ACID

3. 点赞

3.0 效果

  1. 点赞:支持对帖子,评论,评论的评论点赞
  2. 首页点赞数量:统计帖子的点赞数量
  3. 详情页点赞数量:统计点赞数量;显示点赞状态

3.1 为什么需要Redis

因为一篇文章出来之后,可能会有非常多的人给点赞, 需要实时被人看到,所有访问的次数会非常多。

3.2 如何实现点赞操作:

  1. redis直接写Service层,不用写Dao层进行数据访问。(因为是写在内存里面)
  2. 为方便复用创建一个redis工具类
    (1):定义字符常量,方便后面的字符拼接命名
    (2):字符拼接的方法。某个用户的赞和实体(帖子)的赞
  3. 创建LikeService
    (1):点赞方法:执行redisTemplate.execute(new SessionCallback() ,把数据存储到redis中;存储实体赞和用户喜欢赞;需要判断当前是否有赞,再进行赞的存储或者删除。
    (2): 查询某实体点赞的数量,例如帖子,评论,评论的评论
    (3): 查询某人对某实体的点赞状态(返回整数,方便后续如果点踩的情况)
    ///(4):查询某个用户获得的赞—后续的功能
  4. 创建LikeController:点赞的接口
    (1):点赞功能
    (2):查询某实体的点赞数量
    (2):查询用户对该实体点赞的状态(点击两次,可能取消了赞)
    (4):把数据传输给前端(异步请求
  5. 修改HomeController:把缓存中帖子点赞量调用方法加上(使得首页显示正确的赞)
  6. 修改index.html
    (1):修改首页赞的显示
  7. 修改:DiscussPostController:对详情页请求加上方法:记录点赞数量和状态(三层评论都需要加上)
  8. 修改discuss-detial.html
    (1):书写discuss.js,实现点赞/赞的按钮功能,并刷新页面(异步请求)
    (2):修改详情页点赞数量统计

4. 我收到的赞

4.0 效果

  1. 重构点赞功能
    (1):以用户为Key,记录点赞的数量
    (2):increment(key),decrement(key),用于点赞和取消赞
  2. 开发个人主页
    (1):以用户为key,查询点赞数量

4.1 重构点赞和开发个人主页步骤

  1. 在redisUtil中增加一个方法。用于生成用户点赞字符串(故不需要Dao层)
  2. LikeService重构方法点赞,多传入一个实体的id。方便统计实体的赞;添加方法:获取用户的赞。(这里点赞功能需要事务管理,因为赞的状态和存入实体的赞应该需要保持一致的);添加方法:获取当前用户的赞
                    operations.opsForSet().add(entityLikeKey, userId);
                    operations.opsForValue().increment(userLikeKey);
  1. LikeController:修改点赞的功能,多了一个参数
  2. 修改:discuss-detai_html:修改点赞的操作(三个帖子处的点赞);
  3. 修改:discuss.js:点赞的异步请求,多一个参数
  4. UserController:书写个人主页请求
  5. 修改个人主页html:profile.html

5. 关注、取消关注

5.0 需求

  1. 开发关注,取消关注功能
  2. 统计用户的关注数和粉丝数

5.1 核心点

  1. 关注者和被关注者:若A关注了B,那么A是B的粉丝,B是A的目标
  2. 关注的对象可以用户,帖子,题目等,在实现的时候将这些目标抽象为实体。
  3. 关注者和被关注者这些信息也通过Redis查询。

5.2 实现步骤

  1. 修改RedisKeyUtil:添加两个数据:关注者和被关注者(记录两份数据,方便统计)
  2. 新建FolloweService:
    (1)follow关注方法:需要存储关注者和被关注者信息,两份数据,需要保持一致,故使用事务操作
                operations.multi();
                //语句
                return operations.exec();

(2)unfollow取消关注方法:和关注方法相反
3. 新建FolloweController:(异步请求)
(1)关注:调用Service方法:返回json:已经关注
(2)取消关注:调用Service方法:返回json:取消关注
4. 修改Profile.html:关注的逻辑在profile里面。(通过样式进行改变)
5. 修改:在FellowService中添加方法,能够查询更多的信息。(查询是否某用户关注的人和当前用户是否关注某实体)【丰富个人主页内容】
6. 修改:在FollowController添加方法:使得个人主页能够显示更多的信息 【丰富个人主页内容】
7. 修改:profile.html:【丰富个人主页内容】

6. 关注列表、粉丝列表

6.0 实现的功能

点击个人主页里面,点击关注者和被关注者数字显示那里,对应可以进入关注列表和粉丝列表。

6.1 大致思路

  1. 数据持久层Dao层
    由于是频繁使用的数据,故使用Redis进行存储,不涉及数据库操作
  2. 业务层Service层
    (1):查询某个人关注的人,支持分页展示
    (2):查询某个人的粉丝人数,支持分页展示
  3. 表现层Controller层
    (1):处理“查询关注的人”,查询关注的模板
    (2):处理“查询被关注的人”,查询粉丝的模板
    (这里的思路大同小异,就跳过了)

7. 优化登录模块

7. 1 使用Redis存储验证码

7.1.1 原因

  • 验证码需要频繁的刷新与访问,对性能要求很高
  • 验证码不需要持久保存,只需要保存一小会;之前验证码是存进了Session里面
  • 分布式部署的时候,存在Session共享的问题。可能有多个服务器。

7.1.2 实现

  1. 修改RedisKeyUtil:添加验证码的字符名字,用户存储
  2. 修改LoginController:修改之前的逻辑:
    1):修改验证码的存储:getKaptcha
    (1):首先注销方法中的Session引用
    (2):注销把生成的验证码存入Session语句,变为以下:
    (3):验证码归宿:由于在注册或者登录的时候,不能标识用户,需要生成一串字符串,用来标识当前的登录/注册用户,也作为验证码的归宿方。
    (4.):把验证码+归宿作为一个键值对存入缓存中。
        // 验证码的归属(先分配一个随机字符串,标识当前状态)
        String kaptchaOwner = CommunityUtil.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
        cookie.setMaxAge(60);
        cookie.setPath(contextPath);
        response.addCookie(cookie);
        // 将验证码存入Redis
        String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
        redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);  //存活时间设置为60s

1):修改登录:login
(1):检查验证码的逻辑:从Session取值变为从数据库中取值。键为从cookie中拿到的随机生成字符串信息(键值拼接了),值为生成的验证码。

7. 2 使用Redis存储登录凭证

7.2.1 原因

  • 处理每次请求的时候,都需要查询用户的登录凭证token,访问频率非常高。

7.2.2 实现

之前有一张ticket表,可以作废了;

  1. 修改RedisKeyUtil:添加登录凭证对象的字符名字生成
  2. 不需要ticket表了,故删去Dao层:LoginTicketMapper,使用注解:@Deprecated
  3. 修改UserService:注销Dao层LoginTicketMapper
    (1):修改登录方法:把凭证ticket放入Redis中(ticket是一张表信息,会进行序列化为字符串)
    (1):修改登出方法:先获取Redis中的ticket,然后修改里面登出的值,之后再存入
    (1):修改查询凭证方法:通过名字查询Redis中的登录凭证方法。

7. 3 使用Redis缓存用户信息

7.3.1 原因

  • 处理每次请求的时候,都需要查询用户的信息,访问频率高

7.3.2 实现

之前有一个用户信息表,不能够作废,需要持久保存

  1. 修改RedisKeyUtil:添加用户信息对象的字符名字生成
  2. 修改UserService:添加三种方法(主要都是对缓存进行的操作)
    (1):getChache:优先从缓存中查找数据
    (2):initCache:取不到值从数据库中获取值,然后更新到缓存中
    (3):数据库中的信息变更时,直接删除缓存中的数据(不采用更改,是为了避免线程并发带来的问题)
  3. 使用缓存的三个方法(UserService):
    (1)findUserById:先获取Cache,如果不存在,初始化initCache
    (1)activation:在账户激活的时候,修改状态,需要删除缓存:
    (2)updateHeader:更新头部图像,需要进行初始化。
    项目每一个阶段的代码:
    文章参考:
    牛客网的仿牛客项目:https://www.nowcoder.com/study/live/246
    https://blog.csdn.net/qq_43351888/article/details/123943949?spm=1001.2014.3001.5502
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值