发放优惠券下的高并发问题

简介: 工作中遇到的锁时效问题及事务时效问题 排查与解决方案

总结就是 所有敏感的操作 类似跟钱挂钩的 比如发放优惠券的前提就是判断到发放 我们一般都是让一系列操作是原子性 并且别人获取不到这个锁

如果数据不敏感 类似点赞的 那么没有必要 因为一致性必须牺牲高可用的

工厂模式帮我们自动根据传入参数自动执行不同的函数

策略模式帮我们设定了不同的方案 根据传入的参数选择不同的参数去选择不同的方法

首先大体确定问题范围属于并发引起的问题 所以很容易就想到锁了

超卖问题的话

我们只需要确定为什么会超卖

无非就是再发放优惠券的时候没有确定是否已达用户领取上限 或者是 优惠券总发放上限

所以很好解决  无非就是 用上乐观锁的思想 再发放的sql语句加上where条件判断而已

锁失效问题

查询  判断  新增满足这三步之后才会发放优惠券

逻辑上确实没问题 但是我们考虑的只是单机

如果我们并发前提下 A线程执行新增之前 B执行到判断了

这个时候就会出现 同时满足的情况

所以实际上也就是 查询判断新增这三步核心逻辑 我们分开写了 没有使他们同时执行 不具备原子性,

所以我们直接考虑用 Synchronized实现

锁方法太大了 太慢了 所以我们考虑锁方法块  锁的对象由于我们知道Long存在常量池复用-127到128的对象  

所以不能直接锁long  要锁实际值 所以使用apiuserId.toString().intern()来最终解决这个问题

事务边界问题

如果我们开启事务之后 才获取锁 然后释放锁 再提交事务

这个时候就会出现释放锁之后 还没提交事务 其他线程拿到锁 访问了资源 也就是脏读问题

那么只需要换个思考方式 那么我就提交好了再释放锁 不就可以了嘛

所以很简单 就是先获取锁 开启事务 提交事务 释放锁

事务失效问题

那么都用到事务了 事务肯定就会存在失效的情况

排查

1.事务方法非public修饰  spring会有方法判断方法如果不是public进不去

2.非事务方法调用事务方法  因为事务底层是aop生成动态代理 我们当前类调用实际上是this去调用 这个类似成员变量赋值都是this.set get

那么我们非事务 自然aop不会帮我们生成动态代理对象 所以也就不会检测到事务了 所以也就失效了 解决方案就是 手动调用aop对象去调用方法 也就是 aop(动态代理对象).方法名

3.事务方法的异常被捕获 被捕捉了 自然没异常就不会回滚

4事务异常类型不对 类型不对 也就不会检测到 也就相当于没回滚了

5事务传播行为不对 事务之间是有隔离性的  那么A回滚了 是不会影响B的 因为B是独立于A的 当然如果这里是嵌套事务的话 那么就大的会影响小的  小的不会影响大的

6.没有被Spring管理  如果类都没被spring管理 aop都没办法帮你创建代理对象 那就肯定失效了

集群锁失效问题

我们使用的Synchronized底层是基于jvm的锁监视器去做的

当我们部署了多个实例 那么就会有多个jvm

所以自然每个jvm的锁监视器就不一样了 那么这个时候实际上 就会有多个user2的锁

解决方案 类似seata记录表 不再基于jvm的锁监视器 而是基于数据库表 也就是设置分布式锁

分布式锁问题

我们一开始是加锁后再设置过期时间 那么如果加锁后宕机了呢 对吧 实际上还是回到两个操作是分开的 解决方案就是让两个操作有原子性 一起执行

加锁同时设置过期时间指令:SET key value NX EX seconds

那么设置了过期时间后 就会出现当线程1阻塞的时候 锁A过期了

所以这个时候别的线程2就可以获取到锁A 这个时候 开始执行业务

但是这个时候线程1醒过来了 继续删除释放锁 就会导致线程3可以拿到锁了 就会出现又并发了

解决方案 我们设置线程名字为锁key  比如 key为 Thread1 加上userid 这种唯一标识

删除之前判断是否当前线程的 如果不是就不删

记得把判断跟删除 作为一个操作 不然又会出现极端情况了

后面我们还使用了redisson来作为我们的锁

因为他支持重入,看门狗等 而且使用更加简单

还引入了工厂模式来帮我们自然化 根据用户传入的锁类型支持不同的获取锁的对象

后面引入策略模式来帮我们解决多种获取失败方案 解决代码冗余问题

相关文章
|
6月前
|
NoSQL Java 数据库
优惠券秒杀案例 - CAS、Redis+Lua脚本解决高并发并行
优惠券秒杀案例 - CAS、Redis+Lua脚本解决高并发并行
311 0
|
6月前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
|
6月前
|
Java
在高并发环境下,再次认识java 锁
在高并发环境下,再次认识java 锁
71 0
|
6月前
|
消息中间件 NoSQL Java
Java高级开发:高并发+分布式+高性能+Spring全家桶+性能优化
Java高架构师、分布式架构、高可扩展、高性能、高并发、性能优化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分布式项目实战学习架构师之路
|
5月前
|
缓存 NoSQL Java
Java高并发实战:利用线程池和Redis实现高效数据入库
Java高并发实战:利用线程池和Redis实现高效数据入库
500 0
|
3月前
|
监控 算法 Java
企业应用面临高并发等挑战,优化Java后台系统性能至关重要
随着互联网技术的发展,企业应用面临高并发等挑战,优化Java后台系统性能至关重要。本文提供三大技巧:1)优化JVM,如选用合适版本(如OpenJDK 11)、调整参数(如使用G1垃圾收集器)及监控性能;2)优化代码与算法,减少对象创建、合理使用集合及采用高效算法(如快速排序);3)数据库优化,包括索引、查询及分页策略改进,全面提升系统效能。
51 0
|
5月前
|
存储 NoSQL Java
探索Java分布式锁:在高并发环境下的同步访问实现与优化
【6月更文挑战第30天】Java分布式锁在高并发下确保数据一致性,通过Redis的SETNX、ZooKeeper的临时节点、数据库操作等方式实现。优化策略包括锁超时重试、续期、公平性及性能提升,关键在于平衡同步与效率,适应大规模分布式系统的需求。
175 1
|
4月前
|
算法 Java 调度
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
|
4月前
|
监控 网络协议 Java
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
75 0
|
4月前
|
设计模式 安全 NoSQL
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
Java面试题:设计一个线程安全的单例模式,并解释其内存占用和垃圾回收机制;使用生产者消费者模式实现一个并发安全的队列;设计一个支持高并发的分布式锁
71 0
下一篇
无影云桌面