• 仿牛客网项目第四章: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的对象是谁
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    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
    • 2
    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();
    
    • 1
    • 2
    • 3

    (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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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
  • 相关阅读:
    前端框架Vue2.0+Vue3.0学习笔记01
    docker修改容器配置文件的三种方法
    maven私服搭建
    ​Base64编码知识详解 ​
    java培训之InitBinder注解
    QProcess 调用 ffmpeg来处理音频
    关于电脑卡死如何开机、F8、安全模式
    Vue 之 生命周期
    C++ Reference: Standard C++ Library reference: C Library: cstring: strncpy
    C# 要被淘汰了?!
  • 原文地址:https://blog.csdn.net/qq_42974034/article/details/126023365