沉梦手记 沉梦手记
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 数据库基础
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • 网络通信
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Axios入门
  • Vue Router入门
  • Pinia入门
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 运维与部署
  • 音视频处理
  • 随笔
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
Github (opens new window)

沉梦听雨

时间是最好的浸渍剂,而沉淀是最好的提纯器🚀
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 数据库基础
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • 网络通信
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Axios入门
  • Vue Router入门
  • Pinia入门
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 运维与部署
  • 音视频处理
  • 随笔
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
Github (opens new window)
  • 脚手架搭建
  • 瑞吉外卖

  • 黑马点评

  • vue-blog

  • 沉梦接口开放平台

  • 用户中心

  • 聚合搜索平台

  • 仿12306项目

    • 基础业务与登录认证
    • 超卖与分布式锁
      • synchronized 关键字
      • Redis 实现分布式锁
        • 分布式锁常见的问题
        • 问题一:没拿到锁的线程把别人的锁给删了
        • 问题二:过期时间短导致锁失效,两个线程一起执行任务,最后超卖
      • 使用 Redisson 看门狗解决锁超时的问题
        • 问题三:Redis 宕机导致锁互斥失效
        • 问题四:使用红锁可能带来的问题
      • 机器人刷票问题
    • 微服务组件
    • 缓存与前端
    • 项目技术亮点
  • 壁纸小程序项目

  • RuoYi-Vue

  • 项目笔记
  • 仿12306项目
沉梦听雨
2024-07-29
目录

超卖与分布式锁

# 超卖与分布式锁

需配合 JMeter 压力测试

# synchronized 关键字

直接用关键字 synchronized 给方法体加锁,会有两个问题:

  1. 锁的粗粒度太大,会导致售卖效率不高(可以容忍)
  2. 在多节点(多台服务器)的情况下,还是会出现【超卖】(不能容忍)

可以用 MySQL 数据库来加锁,但是性能不高。

# Redis 实现分布式锁

使用类型:String 类型

setIfAbsent 就是对应 Redis 的 setnx

// 获取分布式锁
String lockKey = RedisKeyPreEnum.CONFIRM_ORDER + "-" + DateUtil.formatDate(dto.getDate()) + "-" + dto.getTrainCode();
// setIfAbsent就是对应redis的setnx
Boolean setIfAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, lockKey, 10, TimeUnit.SECONDS);
1
2
3
4

# 分布式锁常见的问题

# 问题一:没拿到锁的线程把别人的锁给删了

如果加锁的动作放在 try 里,释放锁的操作放在 finally,并且用户没拿到锁之后抛出异常,finally 里面依旧释放锁,就会导致 没拿到锁的线程把别人的锁给删了 的问题。

把释放锁移出 finally 不是最优解,解法有以下两种:

  1. 加锁的动作不要放在 try 里
  2. 加锁时,将当前线程 ID 放到锁对应的 value 中,删除时,先去获取 value,比对 value 值和当前线程 ID 一致才能删除

# 问题二:过期时间短导致锁失效,两个线程一起执行任务,最后超卖

解决方案有:"看门狗"

假设过期时间为 60 秒,当倒计时到 30 秒的时候,自动刷新过期时间,所以永远不会出现超时问题。

如果主线程出现异常,锁的过期时间会一直刷新吗?

不会。这是使用守护线程的好处。

使用守护线程的好处是:会随主线程的结束而结束,所以不会出现一直重置成 60 秒、永不过期的问题。

使用守护线程的方式不需要手写,可以集成 Redisson。

# 使用 Redisson 看门狗解决锁超时的问题

Redisson 可以实现分布式锁,并且自带"看门狗"方案。(项目中比较常用)

image

image

# 问题三:Redis 宕机导致锁互斥失效

在 Redis 集群中,Redis 宕机会导致锁互斥失效的问题。

比如,线程 a 拿到了锁,然后执行任务。突然 Redis 宕机了,然后 Redis 集群重新选出了主节点,线程 b 此时发起请求拿到了锁,这就导致了锁互斥失效的问题。

解决方案:使用 Redis 红锁(实际项目中不常用)

红锁的获取方式:

  • 假设现在有 5 台机器,那么线程 1 需要获取 ceil(5/2) = 3 把锁才算获取锁成功。这样即使有某台 Redis 宕机了,其他线程也获取不了锁了,从而解决 Redis 宕机问题
  • 获取的时候还需按顺序获取,以避免造成死锁问题
  • 锁宕机之后,机器不能马上重启。假设超时时间为 5 秒钟,则重启时间至少也为 5 秒钟,获取锁的时间也不能超过 5 秒钟

**当锁宕机后,机器不能马上重启或者切换主备。**这是因为锁可能处于一种不确定的状态,如果机器在锁未释放的情况下重启,可能会导致数据不一致或其他问题。

假设超时时间为 5 秒钟,则重启时间至少也为 5 秒钟,这是为了确保在重启之前,锁有足够的时间超时释放。如果重启时间太短,可能会导致锁在机器重启后仍然被占用,从而导致其他线程无法获取锁。

获取锁的时间也不能超过 5 秒钟,这是为了避免线程在获取锁时出现长时间的阻塞。如果获取锁的时间太长,可能会导致其他线程长时间等待,从而影响系统的性能和响应时间。

可阅读:如何解决集群情况下分布式锁的可靠性 | cmty256.github.io (opens new window)

# 问题四:使用红锁可能带来的问题

  1. 性能问题:红锁需要在多个 Redis 节点上进行通信和协调,这可能会导致性能下降,特别是在高并发环境下
  2. 复杂性增加:红锁的实现相对复杂,需要处理多个节点之间的通信、故障检测和恢复等问题,增加了系统的复杂性
  3. 数据不一致性:在分布式环境中,由于网络延迟、节点故障等原因,可能会导致锁的获取和释放出现不一致的情况,从而影响系统的正确性
  4. 单点故障:如果 Redis 节点出现故障,可能会导致整个分布式锁系统不可用,从而影响系统的可用性
  5. 成本问题:使用 Redis 分布式锁需要额外的 Redis 节点和网络资源,增加了系统的成本

# 机器人刷票问题

使用 令牌大闸 防止机器人抢票:

  • 分布式锁和限流都不能解决机器人刷票的问题,1000 个请求抢票,900 个限流快速失败,另外 100 个有可能是同一个人在刷库
  • 没有余票时,需要查库存才能知道没票,会影响性能,不如查令牌余量来得快
上次更新: 2026/10/10 17:43:40
基础业务与登录认证
微服务组件

← 基础业务与登录认证 微服务组件→

Theme by Vdoing | Copyright © 2023-2026 沉梦听雨 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式