Redis应用问题及分布式锁使用说明

 更新时间:2026年03月07日 15:33:17   作者:祁仙森  
文章主要讨论了缓存穿透、缓存雪崩和分布式锁在高并发场景下的解决方案,包括对空值缓存、设置白名单、布隆过滤器监控、预先设置热门数据、实时调整过期时间、使用锁机制以及构建多级缓存架构等

缓存穿透

现象

  1. 应用服务器压力变大了,很多请求过来
  2. redis命中率降低
  3. 一直查询数据库(缓存中很多数据查询到,就查数据库,导致数据库压力增加)

原因

  • Redis查询缓存中不存在的数据
  • 出现很多非正常的url访问,一般出现在收到恶意攻击的时候,根据正常的url来改变值来恶意攻击服务器

解决方案

  • 对空值缓存:对查不到的数据做空值缓存,并设置空结果的过期时间,最长不超过五分钟
  • 设置可访问的名单(白名单):使用Redis的bitmaps类型定义一个可访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmaps里面的id进行比较,不在的话,做拦截处理
  • 采用布隆过滤器
  • 进行实时监控:人为的排查分析

缓存击穿

现象特点

  1. 数据库访问压力瞬时增大
  2. redis里面没有出现大量key过期
  3. redis正常运行

原因

Redis某个key过期了,大量访问使用这个key

解决方案

  1. 预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大key的时长
  2. 实时调整:现场监控热门数据,实时调整key的过期时长
  3. 使用锁(能避免这种情况,但是去点在于效率低下)
  • 在缓存失效的时候(判断拿出来的值为空),不是立即去load db
  • 先使用缓存工具的某些带成功的操作返回值的操作(比如setnx)去set一个mutex key
  • 当操作返回成功时,在进行load db的操作,并设缓存,最后删除mutex key
  • 当操作返回失败时,证明有现成在load db操作,当前线程睡眠一段时间再重试整个get缓存的方法

缓存雪崩

现象

  1. 数据库压力变大,服务器崩溃

原因

在极少时间段,查询大量key的集中过期情况

解决方案

  • 构建多级缓存架构:nginx缓存+redis缓存+其他缓存
  • 使用锁或者队列:用加锁或者队列的方式保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适合高并发的情形
  • 设置过期标志更新缓存,通过缓存reload机制,预先更新缓存,在即将发生大并发前手动触发加载缓存
  • 将缓存失效时间分散开

Redis实现分布式锁

分布式锁实现方案

  • 基于数据库实现分布式锁
  • 基于缓存实现(Redis等)
  • 基于Zookeeper

上述实现方式Redis性能最高,Zookeeper最可靠

Redis实现

  1. 最简单的使用setnx上锁,del解锁,但是此时有问题,若设置锁了,长时间不释放锁又会导致后续资源拿不到锁的情形
  2. 上述1中锁一直不释放的话,可以设置过期时间来解决这个问题,时间到了自动释放
setnx users name1
expire users 10
  1. 上述2案例有个问题,就是这个不是原子的,设置锁后,突然故障,无法设置过期时间,也会导致问题,因为Redis的操作是原子的,可以使用指令,加锁设置过期时间同时操作
set users name1 nx ex 5

分布式下锁误删除

出现情形

假设a和b两个线程,a线程先获取分布式锁,然后由于业务操作或者服务器卡顿等一系列操作造成a线程的锁过期自动失效释放了(而a线程还没有数据处理完)

a释放锁后,b线程获取了锁执行具体操作,此时a突然操作完了,然后释放了锁,而不是b主动释放的

这种情形就造成了锁误删除的情形

解决方案

  1. 将锁的值设置成uuid,唯一标识,set lock uuid nx ex 10
  2. 释放锁的时候首先判断当前uuid是否和要释放锁的uuid一样,一样释放,不一样不释放

Redis分布式锁——加锁释放锁的原子性优化

造成问题

在上述案例中的当a上锁,执行具体操作的时候,然后释放锁步骤,也遇到了比较锁的uuid值一样,恰巧在del操作要执行之前(也就是比较完成之后,删除操作之前)锁过期自动释放了,然后b线程获取了锁,还在执行具体的操作,a删除的是b的锁,这就造成的分布式下安全隐患

解决方案

在比较删除步骤嵌入原子性的LUA脚本在释放锁的比较释放进行做原子化操作

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Quarkus中RESTEasy Reactive集成合并master分支

    Quarkus中RESTEasy Reactive集成合并master分支

    这篇文章主要为大家介绍了Quarkus中RESTEasy Reactive集成合并master分支的详细作用分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • 使用代码生成器自定义Entity的部分注解

    使用代码生成器自定义Entity的部分注解

    这篇文章主要介绍了使用代码生成器自定义Entity的部分注解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • JVM分析之类加载机制详解

    JVM分析之类加载机制详解

    JVM内部架构包含类加载器、内存区域、执行引擎等。日常开发中,我们编写的java文件被编译成class文件后,jvm会进行加载并运行使用类。本次将对JVM加载部分进行分析,便于大家了解并掌握加载机制
    2022-08-08
  • Java递归基础与递归的宏观语意实例分析

    Java递归基础与递归的宏观语意实例分析

    这篇文章主要介绍了Java递归基础与递归的宏观语意,结合实例形式分析了java递归的相关原理、操作技巧与注意事项,需要的朋友可以参考下
    2020-03-03
  • Spring boot基于ScheduledFuture实现定时任务

    Spring boot基于ScheduledFuture实现定时任务

    这篇文章主要介绍了Spring boot基于ScheduledFuture实现定时任务,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • Java经典快排思想以及快排的改进讲解

    Java经典快排思想以及快排的改进讲解

    今天小编就为大家分享一篇关于Java经典快排思想以及快排的改进讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Jackson 反序列化时实现大小写不敏感设置

    Jackson 反序列化时实现大小写不敏感设置

    这篇文章主要介绍了Jackson 反序列化时实现大小写不敏感设置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • elasticsearch集群cluster主要功能详细分析

    elasticsearch集群cluster主要功能详细分析

    这篇文章主要为大家介绍了elasticsearch集群cluster主要功能详细分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-04-04
  • springboot中使用自定义两级缓存的方法

    springboot中使用自定义两级缓存的方法

    这篇文章主要介绍了springboot中使用自定义两级缓存的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • Spring Mvc中CommonsMultipartFile的特性实例详解

    Spring Mvc中CommonsMultipartFile的特性实例详解

    这篇文章主要给大家介绍了关于Spring Mvc中CommonsMultipartFile特性的相关资料,SpringMVC拥有强大的灵活性,非侵入性和可配置性,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11

最新评论