文章目录

一、什么是 缓存?
⛅为什么用缓存?
⚡如何应用缓存
二、实现一个商家缓存
⌛环境搭建
♨️外围源码
✅测试接口
三、采纳 微服务 Spring Boot 注解开启缓存
✂️@CacheEnable 注解详解
➿调用接口测试
⛵小结

一、什么是 缓存?

缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,个别从数据库中获取,存储于本地代码,例如:

例1:Static final ConcurrentHashMap<K,V> map = new ConcurrentHashMap<>(); 本地用于高并发例2:static final Cache<K,V> USER_CACHE = CacheBuilder.newBuilder().build(); 用于redis等缓存例3:Static final Map<K,V> map =  new HashMap(); 本地缓存

因为其被Static润饰,所以随着类的加载而被加载到内存之中,作为本地缓存,因为其又被final润饰,所以其援用(例3:map)和对象(例3:new HashMap())之间的关系是固定的,不能扭转,因而不必放心赋值(=)导致缓存生效;

⛅为什么用缓存?
一句话总结: 因为应用了缓存后,效率会大大的晋升,缩小了不必要的资源耗费,晋升了用户体验。

然而应用缓存会减少代码复杂度和运维的老本,例如:Redis 集群,多主多从,等等

⚡如何应用缓存
在理论开发中,咱们会构建缓存来晋升零碎的稳固、高可用性,使其性能失去进一步的晋升。最罕用的是 咱们 本地数据与Redis 数据库联合应用

浏览器缓存:次要是存在于浏览器端的缓存

应用层缓存: 能够分为tomcat本地缓存,比方map汇合,或者是应用redis作为缓存

数据库缓存: 在数据库中有一片空间是 buffer pool (缓冲池),增改查数据都会先加载到mysql的缓存中

CPU缓存: 当代计算机最大的问题是 cpu性能晋升了,但内存读写速度没有跟上,所以为了适应当下的状况,减少了cpu的L1,L2,L3级的缓存

二、实现一个商家缓存

需要阐明

本我的项目基于 Spring Boot 整合Redis 并引入 MyBatis-Plus 来实现开发

要求达到第一次加载,查问redis缓存是否存在,若不存在,则查询数据库,查问结束后,存入redis,再次拜访时只获取redis缓存中的数据,不用再次加载数据库,加重数据库压力。

⌛环境搭建
数据库 MySQL 8.0
CCREATE TABLE `tb_shop` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',  `name` varchar(128) NOT NULL COMMENT '商铺名称',  `type_id` bigint(20) unsigned NOT NULL COMMENT '商铺类型的id',  `images` varchar(1024) NOT NULL COMMENT '商铺图片,多个图片以'',''隔开',  `area` varchar(128) DEFAULT NULL COMMENT '商圈,例如陆家嘴',  `address` varchar(255) NOT NULL COMMENT '地址',  `x` double unsigned NOT NULL COMMENT '经度',  `y` double unsigned NOT NULL COMMENT '维度',  `avg_price` bigint(10) unsigned DEFAULT NULL COMMENT '均价,取整数',  `sold` int(10) unsigned zerofill NOT NULL COMMENT '销量',  `comments` int(10) unsigned zerofill NOT NULL COMMENT '评论数量',  `score` int(2) unsigned zerofill NOT NULL COMMENT '评分,1~5分,乘10保留,防止小数',  `open_hours` varchar(32) DEFAULT NULL COMMENT '营业时间,例如 10:00-22:00',  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫',  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫',  PRIMARY KEY (`id`) USING BTREE,  KEY `foreign_key_type` (`type_id`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT
pom依赖
// Mybatis-Plus 外围依赖<dependency>    <groupId>com.baomidou</groupId>    <artifactId>mybatis-plus-boot-starter</artifactId>    <version>3.4.3</version></dependency>// hutool 工具包,各种封装性能 一应俱全<dependency>    <groupId>cn.hutool</groupId>    <artifactId>hutool-all</artifactId>    <version>5.8.5</version></dependency>
外围配置 application.yaml
server:  port: 8082spring:  application:    name: easydp  datasource:    driver-class-name: com.mysql.cj.jdbc.Driver    url: jdbc:mysql://127.0.0.1:3306/db_easy_dp?useSSL=false&serverTimezone=UTC    username: root    password: 111111  redis:    host: redis ip地址    port: 6379    password: redis明码,如没有不写即可    lettuce:      pool:        max-active: 10        max-idle: 10        min-idle: 1        time-between-eviction-runs: 10s  jackson:    default-property-inclusion: non_null # JSON解决时疏忽非空字段mybatis-plus:  type-aliases-package: com.chen.entity # 别名扫描包logging:  level:    com.chen: debug
♨️外围源码
Entity 实体类层
package com.chen.entity;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import lombok.Data;import lombok.EqualsAndHashCode;import lombok.experimental.Accessors;import java.io.Serializable;import java.time.LocalDateTime;/** * @author whc * @date 2022/9/3 10:29 */@Data@EqualsAndHashCode(callSuper = false)@Accessors(chain = true)@TableName("tb_shop")public class ShopEntity implements Serializable {    private static final long serialVersionUID = 1L;    /**     * 主键     */    @TableId(value = "id", type = IdType.AUTO)    private Long id;    /**     * 商铺名称     */    private String name;    /**     * 商铺类型的id     */    private Long typeId;    /**     * 商铺图片,多个图片以','隔开     */    private String images;    /**     * 商圈,例如陆家嘴     */    private String area;    /**     * 地址     */    private String address;    /**     * 经度     */    private Double x;    /**     * 维度     */    private Double y;    /**     * 均价,取整数     */    private Long avgPrice;    /**     * 销量     */    private Integer sold;    /**     * 评论数量     */    private Integer comments;    /**     * 评分,1~5分,乘10保留,防止小数     */    private Integer score;    /**     * 营业时间,例如 10:00-22:00     */    private String openHours;    /**     * 创立工夫     */    private LocalDateTime createTime;    /**     * 更新工夫     */    private LocalDateTime updateTime;    @TableField(exist = false)    private Double distance;}
Mapper长久化层
package com.chen.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.chen.entity.ShopEntity;/** * @author whc * @date 2022/9/3 10:33 */public interface ShopMapper extends BaseMapper<ShopEntity> {    }
ServiceImpl 实现层
package com.chen.service.impl;import cn.hutool.core.bean.BeanUtil;import cn.hutool.core.util.StrUtil;import cn.hutool.json.JSONUtil;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.chen.common.ResultBean;import com.chen.dto.ShopDTO;import com.chen.entity.ShopEntity;import com.chen.mapper.ShopMapper;import com.chen.service.ShopService;import com.chen.utils.RedisConstants;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.Cacheable;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Service;/** * @author whc * @date 2022/9/3 10:36 */@Slf4j@Servicepublic class ShopServiceImpl extends ServiceImpl<ShopMapper, ShopEntity> implements ShopService{    @Autowired    private StringRedisTemplate stringRedisTemplate;    @Override    public ResultBean<ShopDTO> queryById(Long id) {        try {            // 拼接 redis key            String key = RedisConstants.CACHE_SHOP_KEY + id;            //从redis中获取是否已存在,若存在,则间接返回            String json = stringRedisTemplate.opsForValue().get(key);            //判断如果存在,就返回            if (StrUtil.isNotBlank(json)) {                ShopDTO shopDTO = JSONUtil.toBean(json, ShopDTO.class);                return ResultBean.create(0, "success", shopDTO);            }            //从数据库查问数据    getById(id) 是 MyBatis-Plus 提供的查询方法,间接调用即可实现查问            ShopEntity shopEntity = getById(id);            //转换对象            ShopDTO shopDTO = BeanUtil.toBean(shopEntity, ShopDTO.class);            //将数据存入redis            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shopDTO));            return ResultBean.create(0, "success", shopDTO);        } catch (Exception e) {            log.error("获取商品详情失败! e ==> {}", e);            return ResultBean.create(-1, "获取商品详情失败! e ==> {}" + e);        }    }}
Controller层
package com.chen.controller;import com.chen.common.ResultBean;import com.chen.dto.ShopDTO;import com.chen.service.ShopService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;/** * @author whc * @date 2022/9/3 11:06 */@RestController@CrossOrigin@RequestMapping("/shop")public class ShopController {    @Autowired    private ShopService shopService;    @GetMapping("/{id}")    public ResultBean<ShopDTO> queryShopById(@PathVariable("id") Long id) {        return shopService.queryById(id);    }}
工具类
package com.chen.utils;/** * redis key 常量 * @author whc * @date 2022/9/3 13:40 */public class RedisConstants {    public static final String CACHE_SHOP_KEY = "cache:shop:";    public static final Long CACHE_SHOP_TTL = 30L;}
✅测试接口

这里我应用了Redis可视化工具,RESP,地址:https://resp.app/zh/

关上后能够间接连贯你的redis数据库,可视化展现

利用 ApiFox测试接口,可参考 【云原生】前后端拆散我的项目下 如何优雅的联调程序?

第一次调用耗时 1.61s ,是因为咱们第一次redis中无数据,走了查询数据库的操作,而后存入redis,总耗时1.61s

第二次调用

第二次调用间接走的缓存,可见效率晋升了很多!

三、采纳 微服务 Spring Boot 注解开启缓存

开启注解启动缓存

Spring 默认反对缓存,但版本必须在3.1以上,在启动类退出 @EnableCaching 开启即可

package com.chen;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;/** * @author whc * @date 2022/9/3 10:27 *///开启缓存反对@EnableCaching@MapperScan("com.chen.mapper")@SpringBootApplicationpublic class MainApplication {    public static void main(String[] args) {        SpringApplication.run(MainApplication.class, args);    }}
✂️@CacheEnable 注解详解

@CacheEnable: 缓存存在,则应用缓存;不存在,则执行办法,并将后果塞入缓存

ShopServiceImpl 实现类
 @Cacheable(cacheNames = "shop", key = "#root.methodName")    public ShopDTO queryById(Long id) {        try {            String key = RedisConstants.CACHE_SHOP_KEY + id;            String json = stringRedisTemplate.opsForValue().get(key);            if (StrUtil.isNotBlank(json)) {                ShopDTO shopDTO = JSONUtil.toBean(json, ShopDTO.class);                return shopDTO;            }            ShopEntity shopEntity = getById(id);            //转换对象            ShopDTO shopDTO = BeanUtil.toBean(shopEntity, ShopDTO.class);            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shopDTO), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);            return shopDTO;        } catch (Exception e) {            log.error("获取商品详情失败! e ==> {}", e);            return null;        }    }
➿调用接口测试

第一次调用,耗时很长

再次调用,走缓存

查看Redis可视化key

大小 1.11k 字节

再看json存入

大小 653 字节

综上思考,出于内存的起因,咱们抉择应用json存入redis,更省内存!

⛵小结

以上就是微服务 Spring Boot 整合Redis 实战开发解决高并发数据缓存 的简略介绍,缓存是咱们比拟罕用的技术,在解决一些高并发场景下,咱们奇妙的应用缓存能够极大的加重服务器的压力,从而进步零碎的高可用性,Redis基于内存并且是单线程的,所以说十分的快! Redis缓存数据库很重要!