说明:

(1)声明:这个其中的区别和相同点,要清楚;

          ● 在【32:第三章:开发通行证服务:15:浏览器存储介质,简介;】中,前端使用【把“用户基本信息”存到Session Storage中】来减轻后端接口的压力;;;;这是,从前端的角度出发,来减轻后端接口的压力;

          ● 本篇博客,使用Redis缓存用户信息,是利用Redis来减轻访问数据库的压力;

          ● 这两篇博客的内容,其目的差不多,都是降低系统压力,提高并发量;;;;但是,其着力点和具体做法存在差异的;

(2)以前在【Spring Boot电商项目31:商品分类模块十:利用Redis缓存加速响应;】、【附加:【Spring Boot缓存】【Redis】;】介绍过在利用Redis缓存的案例;虽然和本篇博客在技术上存在差异,但其目的是差不多的;有点条条大路通罗马的感觉;

目录

零:本篇博客合理性说明; 

1.问题阐述;

2.使用redis缓存用户信息,减轻数据库压力:简介; 

一:使用redis缓存用户信息,以减轻数据库的压力;

1.修改getUser()方法的逻辑:redis中有用户信息,就从redis中拿;redis中没有用户信息,就从数据库中查,同时把查到的用户信息存到redis中去;

2.当调用【修改/完善用户信息,接口】修改了用户信息后,我们也要及时的去更新redis中的用户信息;

二:效果;

step1:第一次注册/登录用户;

step2:去更新/完善用户信息;

一个前端的、未解决的问题;

step3:Redis缓存效果演示;


零:本篇博客合理性说明; 

1.问题阐述;

(1)我们在【32:第三章:开发通行证服务:15:浏览器存储介质,简介;】中,已经介绍了浏览器存储介质,其中在针对【获得用户基本信息,接口】,在前端使用了Session Storage这个存储介质;;;当时的逻辑是:

          ● 前端第一次想要获取“用户基本信息”的时候,其会去调用后端的【获得用户基本信息,接口】接口;

          ● 同时,我们在前端代码中编写了对应的代码,把获得的“用户基本信息”存储到了Session Storage中;

          ● 后面,当前端第二次想要获取“用户基本信息”的时候,其就不会去调用后端的【获得用户基本信息,接口】接口了,而是从Session Storage中获取;


(2)上面我们在前端的角度,利用了Session Storage的手段,去减轻了接口的压力;提高了系统的抗并发能力;

(3)同时,我们也可以从后端的角度出发;;;利用Redis缓存用户信息数据,来减轻数据库的压力,从而提高系统的抗并发能力;

(4)PS:使用Session Storage存储用户基本信息时的一个问题:用户在第一次请求的时候,数据存在了Session Storage中;;;但是用户如果更改了信息,而此时由于浏览器中有Session Storage,那么此时用户再去获取信息的时候,获取的就是一些过时的数据了:PS:虽然自己对前端代码不太了解,但是经过实测,每次我们更新信息后,前端都会清除掉在Session Storage存放的旧的用户信息;

2.使用redis缓存用户信息,减轻数据库压力:简介; 

(1)原本,每次当我们需要调用【获得用户账户信息,接口】和【获得用户基本信息,接口】的时候,都是需要去访问数据库的;;;但是,如果我们把完整的用户信息缓存到Redis中;;;那么,再需要调用【获得用户账户信息,接口】和【获得用户基本信息,接口】以获得用户信息的时候,其就可以从redsi中拿,而不用再从数据库中取了;


一:使用redis缓存用户信息,以减轻数据库的压力;

1.修改getUser()方法的逻辑:redis中有用户信息,就从redis中拿;redis中没有用户信息,就从数据库中查,同时把查到的用户信息存到redis中去;

修改我们在【user】用户微服务中的、UserController中定义的、根据userId去查user的,getUser()方法的逻辑;

    /**
     * 公用方法:根据userId去查user;
     * @param userId
     * @return
     */
    private AppUser getUser(String userId) {
        // 说明:由于“用户信息”不太常会变动;对于一些千万级别的网站来说,当某个用户第一次查询用户信息的时候,
        // 这些信息我们完全可以使用redis缓存起来;这样一来,用户后续再获取信息的时候,就可以从redis中
        // 获取,就不用查询数据库了;
        AppUser user = null;
        //(1)去redis中查一查,看redis中是否已经有了这个用户的用户信息;
        String userJson = redisOperator.get(REDIS_USER_INFO + ":" + userId);

        //(2.1)如果redis中,已经有了该用户的信息:那么我们就把JSON中用户信息,转成对应的AppUser对象;
        if (StringUtils.isNotBlank(userJson)) {
            user = JsonUtils.jsonToPojo(userJson, AppUser.class);
        } else {
            //(2.2)如果redis中,没有该用户的信息(那么,极大概率是,用户这是第一次来查询用户信息),那么我们
            // 就查询数据库,去数据库中获取用户信息;
            user = userService.getUser(userId);
            //同时,把查到的用户信息,存到redis中去;(以便,让用户在后面第二次想要查询用户信息的时候,就
            // 可以直接从redis中拿了,就不用再操作数据库了)
            redisOperator.set(REDIS_USER_INFO+":"+userId,JsonUtils.objectToJson(user));
        }
        //(2.3)返回user用户对象
        return user;
    }

说明:

(1)修改内容;

(2)逻辑分析;

          ● 看注释;

          ● 因为,我们这儿要操作redis,所以这个类需要注入RedisOperator这个工具类的对象;

          ● 我们在向redis中存用户信息的时候,其key是【redis_user_info:用户id】这种格式;

2.当调用【修改/完善用户信息,接口】修改了用户信息后,我们也要及时的去更新redis中的用户信息;

UserServiceImpl中的updataUserInfo()方法;

    /**
     * 修改/完善用户信息,并且激活用户;
     *
     * @param updateUserInfoBO
     */
    @Override
    public void updateUserInfo(UpdateUserInfoBO updateUserInfoBO) {

        //把前端传过来的updateUserInfoBO中的属性值,copy到一个AppUser对象中去;
        AppUser userInfo = new AppUser();
        BeanUtils.copyProperties(updateUserInfoBO, userInfo);

        //重新设置其更新时间
        userInfo.setUpdatedTime(new Date());
        //设置其用户状态,把其状态设为1(即已激活);
        userInfo.setActiveStatus(UserStatus.ACTIVE.type);

        //和mybatis-plus的套路基本一样,我们使用updateByPrimaryKeySelective()方法(这个方法只去更新数据库表中那些userInfo中有的,,没有的不会动);
        // 而不使用updateByPrimaryKey();(这个方法会全部更新数据库表的内容,,userInfo没有的,就会设为空了),
        int result = appUserMapper.updateByPrimaryKeySelective(userInfo);
        if (result != 1) {//如果上面方法的返回值不为1,就表示这个更新操作出了问题,那么我们就抛出一个异常;
            GraceException.display(ResponseStatusEnum.USER_UPDATE_ERROR);
        }

        //能走到这一步,说明用户信息更新是OK的;那么,此时我们去获取最新的用户信息,把其存到redis中去;
        String userId = updateUserInfoBO.getId();
        AppUser user = getUser(userId);
        redisOperator.set(REDIS_USER_INFO+":"+userId, JsonUtils.objectToJson(user));//这个会覆盖掉旧值
    }

说明: 

(1)修改内容;

          ● 因为,我们这儿要操作redis,所以这个类需要注入RedisOperator这个工具类的对象;同时,我们在向redis中存用户信息的时候,其key的格式,需要按照【redis_user_info:用户id】这种格式;


二:效果;

先全局install一下整个项目,然后启动【user】用户微服务的主启动了;

同时,为了防止任何干扰,我们清除了浏览器缓存、数据库数据、redis中数据;

step1:第一次注册/登录用户;

我们第一次注册/登录个新用户;

然后,会进入账号设置页面;

我们放开断点;因为,accountInfo.html页面,会同时访问【获得用户账户信息,接口】和【获得用户基本信息,接口】;所以,其在后端,其实会调用两次getUser()方法的;所以,这儿的断点我们需要放开两次(在IDEA中,可以按F9),这样一来accountInfo.html页面需要的数据才能准备好,这个页面才能完全加载好;

step2:去更新/完善用户信息;

……………………………………………………

自然,我们更新用户信息后,accountInfo.html页面会重新加载;

自然,因为要获取“用户账户信息”(通过访问【获得用户账户信息,接口】获得),,,也要获得“用户基本信息”(Session Storage中有,就从Session Storage中拿;;;Session Storage中没有,就通过访问【获得用户基本信息,接口】获得;;;因为,在更新用户信息的时候,前端代码把浏览器中的Session Storage存的用户基本给清掉了,,,所以这儿也是需要调用【获得用户基本信息,接口】的);;;;所以,其在后端,其实会调用两次getUser()方法的;

这儿的断点我们需要放开两次(在IDEA中,可以按F9),这样一来accountInfo.html页面需要的数据才能准备好,这个页面才能完全加载好;

PS:因为在accountInfo.html上,(如果Session Storage中没有“用户基本信息”的话) 会调用两次后端的getUser()方法;;;这不太方便我们观察效果;;;所以,我们将在首页上去测试;

下面就是首页;

一个前端的、未解决的问题;

某个页面在session storage中创建的“用户基本信息”,其他页面使用不了吗?(这是前端的知识~~~~,自己不太能搞懂)

(1)情况描述;

然后,

但是,当我们点击左上角的【慕课新闻】后,进入index.html页面;

(2)疑问;
问题1:不是说好的,index.html页面只会尝试去获取“用户基本信息”吗(而不会获取“用户账户信息”);;;;既然,是获取“用户基本信息”;;;;我们前面已经把用户基本信息存到Session Storage中了;;;;其为什么还要调用后端的接口;
问题2:为什么【我们在accountInfo.html这个页面上,保存到Session Storage中的“用户基本信息”】,在index.html页面上查看,却不在了? 

(3)交代:

          ● 讲道理,这个(主要是前端范围的内容)问题自己不是很懂,真要搞明白,估计需要花费很大的精力~~~算了,暂时搁置的~~

          ● 这儿没有错,主要是对于Session Storage自己在上篇博客中,一开始理解错了;;Session Storage不能跨窗口;具体可以参考上篇博客【32:第三章:开发通行证服务:15:浏览器存储介质,简介;(cookie,Session Storage,Local Storage)】;

step3:Redis缓存效果演示;

(1)Redis中有缓存数据时;

……………………………………………………

F8单步调试,

F9让其走完之后,页面OK了,可以Session Storage中也有了“用户基本信息”;


(2)Redis中没有缓存数据时;

 


(3)如果我们把Session Storage中的用户信息给清掉,再刷新页面的话,其就是从Redis中获取了;

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐