常见的Redis使用问题及解决方案

news2024/11/24 14:25:45

目录

1. 缓存穿透

1.1 解决方案

2. 缓存击穿

2.1 解决方案

3. 缓存雪崩

3.1 概念图及问题描述

​编辑3.2 解决方案

4. 分布式锁

4.1 概念

4.2 基于redis来实现分布式锁

4.3 用idea来操作一遍redis分布式锁

4.4 分布式上锁的情况下,锁释放了服务器b中的锁问题

4.5 LUA保证删除原子性

4.6 分布式锁小总结


1. 缓存穿透

        当我们用户访问浏览器从应用服务器中请求web服务的时候,这时候请求的数据,在redis缓存中访问不到,那么就会去访问数据库,如果redis一直访问不到数据,它就会一直访问数据库,导致数据库压力变大,从而导致数据库崩溃。(缓存穿透)

  • 一般出现缓存穿透的原因:
    1. redis查询不到数据库;
    2. 出现很多非正常url访问(就是一直访问不存在的url路径);

其目的并不是为了得到数据,而是可能黑客利用此漏洞进行攻击,从而压垮数据库,让服务器瘫痪;

比如说一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,被一直访问攻击,就会存在此问题;

1.1 解决方案

        一个一定不存在缓存几查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义;

  • 解决:

    (1) 对空值缓存:如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(nul)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟;

    (2) 设置可访问的名单(白名单):使用 bitmaps 类型定义一个可以访问的名单,名单id 作为 bitmaps 的偏移量每次访问和 bitmap 里面的 id 进行比较,如果访问 id 不在 bitmaps 里面,进行拦截,不允许访问;

    (3) 采用布隆过滤器:(布隆过滤器(Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个元素是否在一个集合中。"它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难;将所有可能存在的数据哈希到一个足够大的 bitmaps 中,一个一定不存在的数据会被 这个 bitmaps 拦截掉,从而避免了对底层存储系统的查询压力;

    (4) 进行实时监控:当发现 Redis 的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务;

2. 缓存击穿

  • 缓存击穿问题起因:
    1. 数据库访问压力瞬时增加
    2. redis里面没有出现大量key过期
    3. redis正常运行

问题描述:

        key对应的数据存在,但在redis中过期,此时若有大量并发请求过滤,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能回瞬间把后端DB压垮;

2.1 解决方案

(1) 预先设置热门数据:在 redis 高峰访问之前,把一些热门数据提前存入到redis 里面,加大这些热门数据 key 的时长;

(2) 实时调整:现场监控哪些数据热门,实时调整 key 的过期时长;

(3) 使用锁:

  1. 就是在缓存失效的时候(判断拿出来的值为空),不是立即去 load db;
  2. 先使用缓存工具的某些带成功操作返回值的操作(比如 Redis 的 SETNX)去set一个mutex key;
  3. 当操作返回成功时,再进行load db的操作,并回设缓存,最后删除mutex key;

(4) 当操作返回失败,证明有线程在load db,当前线程睡眠一段时间再重试整个get缓存的方法;

3. 缓存雪崩

3.1 概念图及问题描述

        key 对应的数据存在,但在 redis 中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端 DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮;

缓存雪崩与缓存击穿的区别在于这里针对很多 key 缓存,前者则是某一个 key;

缓存失效时的雪崩效应对底层系统的冲击非常严重;

3.2 解决方案

采用构建多级缓存架构:

通过其它缓存来建立多道城墙,当nginx缓存不行了,换到redis缓存,以此类推。

(1) 构建多级缓存架构:nginx缓存+redis 缓存 +其他缓存(ehcache等);

(2) 使用锁或队列:用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。但是不适用高并发情况;

(3) 设置过期标志更新缓存:记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际 key 的缓存;

(4) 将缓存失效时间分散开:比如我们可以在原有的失效时间基础上增加一个随机值,比如 1-5 分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件;

4. 分布式锁

4.1 概念

        随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的 Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨 JVM 的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题;

  • 分布式锁主流的实现方案:

    基于数据库实现分布式锁;

    基于缓存(Redis等);

    基于 Zookeeper;

  • 每一种分布式锁解决方案都有各自的优缺点:

    性能:redis 最高;

    可靠性:zookeeper 最高;

4.2 基于redis来实现分布式锁

设置锁:

释放锁:

避免设置锁的时候服务器挂掉来不及设置过期时间,直接连锁和过期时间一起设置:

手动设置一个锁的过期时间(这样就不需要再次del删除锁就可以设值了):

4.3 用idea来操作一遍redis分布式锁

  1. 给key设置值/numValue
  2. 使用锁机制,去实现资源控制/getLock
package com.lei.my_redis.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class RedisLock {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @GetMapping("/numValue")
    public String numValue() {
        redisTemplate.opsForValue().set("num", "1");
        return "1";
    }

    /**
     * 实现锁机制,如果该锁被持有者使用,未得到释放,那么取余的操作者不得使用到锁
     *
     * @return 打印日志
     */
    @GetMapping("/getLock")
    public String testLock() {

        /*
            1.在 Redis 中,使用 setIfAbsent 方法尝试获取锁时,如果锁的键已经存在(即锁已经被其他客户端持有),那么该操作将失败,
            2.不会重置锁的过期时间或更改锁的值。如果成功获取了锁(即锁的键之前不存在),那么会将锁的键设置为指定的值,并设置一个过期时间
            3.setIfAbsent锁设置:
                         key: 要设置的值
                         value: 要设置的键的值
                         timeout: 过期时间, 如果设置为-1, 表示该键没有过期时间(永久有效), 设置为0, 表示立即过期, 如果是整数, 就是该键在指定单位时间内过期
                         TimeUnit: 过期时间单位, 如 TimeUnit.SECONDS、TimeUnit.MILLISECONDS 等
         */
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock6", "10", 5, TimeUnit.SECONDS);  // 1.获取锁,setnx;

        if (Boolean.TRUE.equals(lock)) {  // 2.获取锁成功,查询num 的值
            Object value = redisTemplate.opsForValue().get("num");  // 2.1判断nun为空,return
            if (ObjectUtils.isEmpty(value)) {
                return "num为空";
            }

            int num = Integer.parseInt(value + "");  // 2.2有值就转成int
            num++;
            redisTemplate.opsForValue().set("num", Integer.toString(num));  // 2.3把redis的num加1
            redisTemplate.delete("lock6");  // 2.4释放锁 del
            return "值设置成功: " + redisTemplate.opsForValue().get("num");
        } else {
            try {
                // Thread.sleep(100);  // 3.获取锁失败,每隔0.1秒再获取
                Thread.sleep(1000);  // 3.获取锁失败,每隔0.1秒再获取
                testLock();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        return "资源正在获取中,请等待...";
    }
}

4.4 分布式上锁的情况下,锁释放了服务器b中的锁问题

问题描述:

解决办法(给锁设置一个uuid):

set lock uuid nx ex 10

设uuid:仅参考

        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock6", uuid, 5, TimeUnit.SECONDS);  // 1.获取锁,setnx;
        // 判断比较uuid值是否一样
        String lockUuid = (String) redisTemplate.opsForValue().get("lock6");
        if (Objects.requireNonNull(lockUuid).equals(uuid)) {
            redisTemplate.delete("lock6");  // 2.4释放锁 del
        }

4.5 LUA保证删除原子性

在我们使用redis继续上锁的过程中是缺少原子性的;

LUA脚本的作用支持java也支持C++,其中支持原子性;

可以用LUA脚本来解决误删key;

4.6 分布式锁小总结

为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

-互斥性:在任意时刻,只有一个客户端能持有锁;

-不会发生死锁:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁;

-解铃还须系铃人:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了;

-加锁和解锁必须具有原子性;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1833278.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

教育培训系统(FastAdmin+ThinkPHP+Unipp)

引领学习新风尚 &#x1f4da; 引言&#xff1a;教育新篇章 随着科技的不断发展&#xff0c;教育形式也在不断创新与变革。教育培训系统作为这一变革的重要载体&#xff0c;正逐渐改变着我们的学习方式。今天&#xff0c;就让我们一起探索教育培训系统的魅力&#xff0c;看看它…

[ARM-2D 专题]3. ##运算符

C语言的宏系统相当强大&#xff0c;它允许使用##符号来处理预处理期的文本替换。这种用法被称为标记连接&#xff08;token pasting&#xff09;操作&#xff0c;其结果是将两个标记紧紧地连接在一起&#xff0c;而省略掉它们之间的所有空格。在复杂的宏定义中&#xff0c;运用…

部分CVE复现Web(1)

Apache HTTP Server 路径穿越漏洞CVE-2021-41773 ​ 首先&#xff0c;先来看一下这个漏洞的官方描述&#xff1a; ​ CVE-2021-41773 是在 Apache HTTP Server 2.4.49 中对路径规范化所做的更改中发现了一个缺陷。攻击者可以使用路径遍历攻击将 URL 映射到预期文档根目录之外的…

三星(中国)投资公司线上入职测评笔试邀请数字推理语言逻辑真题题库

三星&#xff08;中国&#xff09;有限公司北京分公司 邀请您参加 SHL线上笔试 具体安排如下&#xff1a; 笔试时间&#xff1a;周三 9:00 笔试时长&#xff1a;1.5h ~ 2h 笔试内容及要求&#xff1a;数字推理限时30min&#xff1b;语言逻辑限时30min&#xff1b;性格测试不…

国内外典型的知识图谱项目

文章目录 早期的知识库项目互联网时代的知识图谱中文开放知识图谱垂直领域知识图谱 从人工智能的概念被提出开始&#xff0c;构建大规模的知识库一直都是人工智能、自然语言理解等领域的核心任务之一。下面分别介绍早期的知识库项目、互联网时代的知识图谱、中文开放知识图谱和…

什么是Vue开发技术

概述 Vue.js 是一个用于构建用户界面的渐进式框架&#xff0c;它设计得非常灵活&#xff0c;可以轻松地被集成到任何项目中。 vue是视图的发音&#xff0c;其目的是帮助开发者易于上手&#xff0c;提供强大的功能构建复杂的应用程序 示例 以下是vue基本的语法概述 声明式渲…

激活和禁用Hierarchy面板上的物体

1、准备工作&#xff1a; (1) 在HIerarchy上添加待隐藏/显示的物体&#xff0c;名字自取。如&#xff1a;endImage (2) 在Inspector面板&#xff0c;该物体的名称前取消勾选&#xff08;隐藏&#xff09; (3) 在HIerarchy上添加按钮&#xff0c;名字自取。如&#xff1a;tip…

“深入探讨Redis主从复制:原理、配置与优化“

目录 # 概念 1. 配置主从同步步骤 1.1 创建文件夹 1.2 复制配置文件 1.3 配置文件关闭 1.4 查看端口号&#xff0c;发现端口号存在 1.5 连接三个端口号 1.6 查看主机运行情况 1.7 让服务器变成&#xff08;主机&#xff09;或&#xff08;从机&#xff09; 1.8 实现效…

HarmonyOS之自选股App

支持在 鸿蒙、安卓、苹果设备上运行。 1.界面效果展示 2.数据存储 数据存储采用的是官方的 ohos.data.relationalStore.relationalStore stock_code表用来存储A股市场5000多家公司的股票代码和名称等信息 const TAB_STOCK_CODE "stock_code" const CREATE_TABL…

关于IOMMU问题的扩展

关联CSDN&#xff1a; Steam Deck OLED WLAN下载速率过低问题的排查和解决-CSDN博客 前言 如前所述&#xff0c;Steam Deck OLED WLAN速率低问题和IOMMU有一定的关系&#xff0c;这里我们对IOMMU为什么会对速率有影响进行一个较深入的理解。 对于IOMMU我相信大家通过网上的…

java面试(企业场景)

设计模式 工厂方法模式 简单工厂模式 简单工厂包括以下角色&#xff1a; 抽象产品&#xff1a;定义了产品的规范&#xff0c;描述了产品的主要特性和功能具体产品&#xff1a;实现或者继承抽象产品的子类具体工厂&#xff1a;提供了创建产品的机会&#xff0c;调用者通过该…

自动采集软件||自动采集主流电商商品详情SKU数据价格功能实现||电商API接口的应用

实现自动化淘宝商品数据采集的方法有多种&#xff0c;一种常见的方式是利用网络 Python 技术。您可以编写一个网络 Python程序&#xff0c;通过模拟浏览器发送请求&#xff0c;获取淘宝商品页面的数据&#xff0c;并对数据进行解析和提取&#xff0c;最终存储到数据库或文件中。…

力扣172. 阶乘后的零

Problem: 172. 阶乘后的零 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.要使得末尾出现0&#xff0c;则乘式中必须出现因子2与5&#xff1b; 2.而由于对于一个数的阶乘&#xff0c;易知因子2的个数是大于因子5的个数&#xff08;因为只要出现偶数则可以分解出…

1V升3V升压LED驱动WT7013

1V升3V升压LED驱动WT7013 WT7013是一款专业的高亮度LED驱动芯片&#xff0c;其具备提供1A驱动电流以支持3W的LED设备运行的能力。此款芯片以其高效率和低功耗的特性&#xff0c;使其在适用于使用1到2个碱性电池或者锂电池供电的LED照明设备中表现卓越。 WT7013 还配备有开路保…

DOS INT 21H中断 2号功能暗改AL

注意此时AX0200&#xff0c;DX0057 执行INT 21H之后&#xff1a; 可以看到执行完“??? [BXSI]”之后&#xff0c;AL就变为了57H&#xff0c;和DL相同。 部分INT 21H功能表&#xff1a; 所以究竟是什么原因呢&#xff1f; -------------------------------------------…

Shopee虾皮API:获取商家店铺商品列表

一、平台介绍 Shopee&#xff0c;作为东南亚及中国台湾地区领先的电商平台&#xff0c;为卖家提供了一个便捷、高效的销售渠道。作为卖家&#xff0c;能够将自己的商品展示在Shopee平台上&#xff0c;并通过平台的流量和工具&#xff0c;将商品销售给更多的潜在买家。 为了帮…

s2b2c这个模式如今发展得怎么样了?

在工业时代&#xff0c;每一个客户和消费者都是一个独立的个体&#xff0c;是被动地接受广告和被推送产品的&#xff0c;但在互联网时代&#xff0c;由于信息技术的基础设施和广泛沟通的社交互动&#xff0c;客户已经因为各种各样的社交场景事先聚集到一起。在个性化即大众化的…

基于WPF技术的换热站智能监控系统16--动态数据绑定

1、实现思路 1&#xff09;实时读取到的数据绑定到前台UI控件上&#xff0c;这个通过MVVM模式实现&#xff0c;同时注意实时读取必须通过任务task异步方式&#xff0c;这就需要读取PLC数据。 2&#xff09;UI控件的动作&#xff0c;如开或关水泵&#xff0c;必定能够将值写入…

【教学类-12-12】20240617通义万相-动物图片6张编故事(A4一页4条)

背景需求 【教学类-12-11】20240612通义万相-动物图片连连看&#xff08;A4一页3套&#xff09;-CSDN博客文章浏览阅读891次&#xff0c;点赞34次&#xff0c;收藏11次。【教学类-12-11】20240612通义万相-动物图片连连看&#xff08;A4一页3套&#xff09;https://blog.csdn.n…