Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿

这篇具有很好参考价值的文章主要介绍了Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么叫缓存穿透?

模拟一个场景:

前端用户发送请求获取数据,后端首先会在缓存Redis中查询,如果能查到数据,则直接返回.如果缓存中查不到数据,则要去数据库查询,如果数据库有,将数据保存到Redis缓存中并且返回用户数据.如果数据库没有则返回null;

这个缓存穿透的问题就是这个返回的null上面,如果客户端恶意频繁的发起Redis不存在的Key,且数据库中也不存在的数据,返回永远是null.当洪流式的请求过来,给数据库造成极大压力,甚至压垮数据库.它永远越过Redis缓存而直接访问数据库,这个过程就是缓存穿透.

其实是个设计上的缺陷.

缓存穿透解决方案

业界比较成熟的一种解决方案:当越过缓存,且数据库没有该数据返回客户端null并且存到Redis,数据是为"",看实际情况并给这个Key设置过期时间.这种方案一定程度上减少数据库频繁查询的压力.

实战过程

CREATE TABLE `item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(255) DEFAULT NULL COMMENT '商品编号',
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '商品名称',
  `create_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='商品信息表';

INSERT INTO `item` VALUES ('1', 'book_10010', 'Redis缓存穿透实战', '2019-03-17 17:21:16');

Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿,缓存,redis,数据库

项目整体结构

Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿,缓存,redis,数据库

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>redis1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redis1</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>3.0.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

启动类

@SpringBootApplication
@MapperScan({"com.example.redis1.mapper"})
public class Redis1Application {

    public static void main(String[] args) {
        SpringApplication.run(Redis1Application.class, args);
    }

}

application.yml

server:
  port: 80
spring:
  application:
    name: redis-test
  redis:
    ##redis 单机环境配置
    ##将docker脚本部署的redis服务映射为宿主机ip
    ##生产环境推荐使用阿里云高可用redis服务并设置密码
    host: 127.0.0.1
    port: 6379
    password:
    database: 0
    ssl: false
    ##redis 集群环境配置
    #cluster:
    #  nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003
    #  commandTimeout: 5000
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://1111111:3306/redis-test?useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=GMT%2B8&useCursorFetch=true
    username: xxxx
    password: xxxxxxxx
mybatis:
  mapper-locations: classpath:mappers/*Mapper.xml  # 指定mapper文件位置
  type-aliases-package: com.example.redis1.pojo
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    com.example.redis1.mapper: debug

数据库映射xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.redis1.mapper.ItemMapper" >
  <resultMap id="BaseResultMap" type="com.example.redis1.pojo.Item" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="code" property="code" jdbcType="VARCHAR" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, code, name, create_time
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from item
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from item
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="item" >
    insert into item (id, code, name,
      create_time)
    values (#{id,jdbcType=INTEGER}, #{code,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
      #{createTime,jdbcType=TIMESTAMP})
  </insert>
  <insert id="insertSelective" parameterType="item" >
    insert into item
    <trim prefix="(" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        id,
      </if>
      <if test="code != null" >
        code,
      </if>
      <if test="name != null" >
        name,
      </if>
      <if test="createTime != null" >
        create_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
      <if test="id != null" >
        #{id,jdbcType=INTEGER},
      </if>
      <if test="code != null" >
        #{code,jdbcType=VARCHAR},
      </if>
      <if test="name != null" >
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="createTime != null" >
        #{createTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="item" >
    update item
    <set >
      <if test="code != null" >
        code = #{code,jdbcType=VARCHAR},
      </if>
      <if test="name != null" >
        name = #{name,jdbcType=VARCHAR},
      </if>
      <if test="createTime != null" >
        create_time = #{createTime,jdbcType=TIMESTAMP},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="item" >
    update item
    set code = #{code,jdbcType=VARCHAR},
      name = #{name,jdbcType=VARCHAR},
      create_time = #{createTime,jdbcType=TIMESTAMP}
    where id = #{id,jdbcType=INTEGER}
  </update>

  <!--根据商品编码查询-->
  <select id="selectByCode" resultType="item">
    select
    <include refid="Base_Column_List" />
    from item
    where code = #{code}
  </select>

</mapper>

pojo

package com.example.redis1.pojo;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.util.Date;

@Data
public class Item {
    private Integer id;

    private String code;

    private String name;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date createTime;

}

mapper

package com.example.redis1.mapper;


import com.example.redis1.pojo.Item;
import org.apache.ibatis.annotations.Param;

public interface ItemMapper {
    int deleteByPrimaryKey(Integer id);

    int insert(Item record);

    int insertSelective(Item record);

    Item selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(Item record);

    int updateByPrimaryKey(Item record);

    Item selectByCode(@Param("code") String code);
}

service

package com.example.redis1.service;


import com.example.redis1.mapper.ItemMapper;
import com.example.redis1.pojo.Item;
import com.fasterxml.jackson.databind.ObjectMapper;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 缓存穿透service
 * Created by Administrator on 2019/3/17.
 */
@Service
public class CachePassService {

    private static final Logger log= LoggerFactory.getLogger(CachePassService.class);

    @Autowired
    private ItemMapper itemMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    private static final String keyPrefix="item:";

    /**
     * 获取商品详情-如果缓存有,则从缓存中获取;如果没有,则从数据库查询,并将查询结果塞入缓存中
     * @param itemCode
     * @return
     * @throws Exception
     */
    public Item getItemInfo(String itemCode) throws Exception{
        Item item=null;

        final String key=keyPrefix+itemCode;
        ValueOperations valueOperations=redisTemplate.opsForValue();
        if (redisTemplate.hasKey(key)){
            log.info("---获取商品详情-缓存中存在该商品---商品编号为:{} ",itemCode);

            //从缓存中查询该商品详情
            Object res=valueOperations.get(key);
            if (res!=null&&!(res.equals(""))){
                item=objectMapper.readValue(res.toString(),Item.class);
            }
        }else{
            log.info("---获取商品详情-缓存中不存在该商品-从数据库中查询---商品编号为:{} ",itemCode);

            //从数据库中获取该商品详情
            item=itemMapper.selectByCode(itemCode);
            if (item!=null){
                valueOperations.set(key,objectMapper.writeValueAsString(item));
            }else{
                //过期失效时间TTL设置为30分钟-当然实际情况要根据实际业务决定
                valueOperations.set(key,"",30L, TimeUnit.MINUTES);
            }
        }
        return item;
    }
}

controller

package com.example.redis1.controller;

import com.example.redis1.service.CachePassService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 缓存穿透实战
 * @Author:debug (SteadyJack)
 * @Date: 2019/3/17 18:33
 **/
@RestController
public class CachePassController {

    private static final Logger log= LoggerFactory.getLogger(CachePassController.class);

    private static final String prefix="cache/pass";

    @Autowired
    private CachePassService cachePassService;


    /**
     * 获取热销商品信息
     * @param itemCode
     * @return
     */
    @RequestMapping(value = prefix+"/item/info",method = RequestMethod.GET)
    public Map<String,Object> getItem(@RequestParam String itemCode){
        Map<String,Object> resMap=new HashMap<>();
        resMap.put("code",0);
        resMap.put("msg","成功");

        try {
            resMap.put("data",cachePassService.getItemInfo(itemCode));
        }catch (Exception e){
            resMap.put("code",-1);
            resMap.put("msg","失败"+e.getMessage());
        }
        return resMap;
    }
}

第一次访问

localhost/cache/pass/item/info?itemCode=book_10010

Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿,缓存,redis,数据库

查看日志输出Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿,缓存,redis,数据库

用个数据库不存在的

localhost/cache/pass/item/info?itemCode=book_10012

Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿,缓存,redis,数据库

后端的处理是将不存在的key存到redis并指定过期时间

Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿,缓存,redis,数据库

其他典型问题介绍

缓存雪崩:指的的某个时间点,缓存中的Key集体发生过期失效,导致大量查询的请求落到数据库上,导致数据库负载过高,压力暴增的现象

解决方案:设置错开不同的过期时间

缓存击穿:指缓存中某个频繁被访问的Key(热点Key),突然过期时间到了失效了,持续的高并发访问瞬间就像击破缓存一样瞬间到达数据库。

解决办法:设置热点Key永不过期

缓存预热:一般指应用启动前,提前加载数据到缓存中文章来源地址https://www.toymoban.com/news/detail-837572.html

到了这里,关于Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第十八章_Redis缓存预热+缓存雪崩+缓存击穿+缓存穿透

    缓存预热 缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据。 可以通过@PostConstruct初始化白名单数据 缓存雪崩 发生  redis主机挂了,Redis 全盘崩溃

    2024年02月07日
    浏览(38)
  • Redis7之缓存预热 + 缓存雪崩 + 缓存击穿 + 缓存穿透(八)

    缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据! 使用 @PostConstruct 初始化白名单数据 缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造

    2023年04月08日
    浏览(34)
  • 探讨Redis缓存问题及解决方案:缓存穿透、缓存击穿、缓存雪崩与缓存预热(如何解决Redis缓存中的常见问题并提高应用性能)

    Redis是一种非常流行的开源缓存系统,用于缓存数据以提高应用程序性能。但是,如果我们不注意一些缓存问题,Redis也可能会导致一些性能问题。在本文中,我们将探讨Redis中的一些常见缓存问题,并提供解决方案。 缓存穿透指的是当一个请求尝试访问一个不存在于缓存中的

    2024年02月03日
    浏览(49)
  • Redis - 做缓存时高并发问题:缓存穿透、击穿、雪崩,数据库缓存双写不一致

    当用户访问的数据既不在缓存也不在数据库中时,就会导致每个用户查询都会“穿透” 缓存“直抵”数据库。这种情况就称为缓存穿透。当高度发的访问请求到达时,缓存穿透不 仅增加了响应时间,而且还会引发对 DBMS 的高并发查询,这种高并发查询很可能会导致 DBMS 的崩

    2024年02月04日
    浏览(37)
  • Redis_缓存3_缓存异常(数据不一致、雪崩、击穿、穿透)

    四个方面 缓存中数据和数据库不一致 缓存雪崩 缓存击穿 缓存穿透 14.6.1数据不一致: 一致性包括两种情况 缓存中有数据,需要和数据库值相同 缓存中没有数据,数据库中的数据是最新值 如果不符合以上两种情况,则出现数据不一致的问题。 读写缓存 同步直写 异步写回

    2024年02月13日
    浏览(28)
  • redis 缓存雪崩 && 缓存击穿 && 缓存穿透

    什么是缓存雪崩 当我们提到缓存系统中的问题,缓存雪崩是一个经常被讨论的话题。缓存雪崩是指在某一时刻发生大量的缓存失效,导致瞬间大量的请求直接打到了数据库,可能会导致数据库瞬间压力过大甚至宕机。尤其在高并发的系统中,这种情况会导致连锁反应,整个系

    2024年02月07日
    浏览(37)
  • Redis 缓存穿透、缓存雪崩、缓存击穿

    缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。 常见的解决方案有两种:         缓存空对象                  优点:实现简单,维护方便                 缺点: 额外的内存消耗 可能造

    2024年02月02日
    浏览(33)
  • REDIS缓存穿透 击穿 雪崩

    一、前言   在我们日常的开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题,可是一旦涉及大数据量的需求,比如一些商品抢购的情景,或者是主页访问量瞬间较大的时候,单一使用数据库来

    2024年02月09日
    浏览(30)
  • Redis缓存穿透,雪崩,击穿

    1、定义 缓存 就是数据交换的 缓冲区 ,缓存就是 缓冲区内的数据 ,一般从数据库中获取,存储于本地代码。 由于其被 Static 修饰,所以随着类的加载而被加载到 内存之中 ,作为本地缓存,由于其又被 final 修饰,所以其引用和对象之间的关系是固定的,不能改变,因此不用担心赋值(=

    2024年02月10日
    浏览(37)
  • Redis缓存击穿、雪崩、穿透

    我们通过对数据库的数据进行查询后在redis中缓存,但是在高并发环境下,某个保存的key值在失效的瞬间被大量并发请求访问,这些持续的大并发量就会穿破缓存,直接请求数据库,会对数据库造成巨大压力,这些请求还会使数据多次写入缓存,多了许多不必要的操作。    

    2024年02月08日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包