秒杀系统注意事项(限时+限流+ZooKeeper/Redisson+redis+隐藏接口+乐观锁)

本文探讨了秒杀系统中常见的技术挑战,包括限时与限流策略,以及如何使用ZooKeeper、Redisson和Redis解决并发问题。提到了避免使用悲观锁以提高效率,并详细介绍了乐观锁在防止超卖问题中的应用。同时,文章讨论了在分布式环境中,JVM缓存的一致性问题,提出使用Zookeeper分布式锁和Redisson作为解决方案。最后,强调了在性能优化中,锁的粒度控制和代码优化的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 新建的springboot启动程序包org.junit.jupiter.api不存在:检查配置文件是否引入依赖
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
        </dependency>
  • jmeter不建议直接使用GUI:
Don't use GUI mode for load testing !, only for Test creation and Test debugging.
For load testing, use CLI Mode (was NON GUI):
   jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
& increase Java Heap to meet your test requirements:
   Modify current env variable HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" in the jmeter batch file
Check : https://jmeter.apache.org/usermanual/best-practices.html
  • synchronized同步锁(这种悲观锁的方式并不推荐,会让线程一个个排队等待,降低效率)

         (14条消息) synchronized详解_三分恶的博客-优快云博客_synchronizedhttps://blog.youkuaiyun.com/sinat_40770656/article/details/113784150

  •  synchronized注意事项:
  1. 不能直接加在业务层的方法上 ,在业务层加了事务控制的(@Transactional),加入事务控制会导致事务也会有线程同步,以及synchronized也会有一个线程同步,事务的范围比线程同步的范围大,synchronized确实可以保证线程一个一个走,但是可能出现,synchronized结束后,事务没有结束,锁释放了,事务却没有结束,可能出现多提交的问题。
  2. 解决方法:1)可以把@Transactional注释掉  2)将synchronized加在控制器调用service方法大的时候使用,此时synchronized锁的范围比@Transactional范围大。比如:
synchronized(this) {
    int orderId = orderService.kill(id);
}
  •  乐观锁解决超卖问题,实际上是把实际的超卖问题交给数据库来解决,利用数据库中方定义的version字段,以及数据库中的‘事务’实现在并发条件下商品的超卖问题
  • redis缓存库存已减1,数据库更新以及创建订单失败,导致redis缓存与数据库不一致,在创建订单失败的情况下需要恢复redis库存
  • 分布式环境jvm缓存同步问题:机器a在某时刻卖完了,设置ConcurrentHashMap为true;b访问数据库存为0,将本机ConcurrentHashMap也设置为true,此时机器a在创建订单时出错Catch Exception 并将ConcurrentHashMap与库存恢复(恢复后库存将不是0);但是机器b没有Catch Exception,机器b的ConcurrentHashMap依旧为true表示售空,无法进行下面的操作。

     jvm级别的缓存在分布式集群环境中如何达到一致性的  解决办法:zookpeer分布式锁,监听

  • zookeeper安装版本必须与pom.xml版本一致
  • redis分布式锁,需要解决的问题:
  1. 加锁后执行过程中出异常,会出死锁问题,需要try catch,不遇到什么错,finally释放锁;
  2. 加完锁宕机了,没办法finally释放锁?给锁加时长(直接使用setAbsent(k,v,time)命令一次性执行,不要使用加锁再设置时长,万一加完锁还没设置时长宕机了,会死锁)。
  3. 加时长,可能也会出现超卖问题,ex:第一个请求执行时间超过加锁时长,第二个请求过来加锁,第一个请求此时执行完删除锁(此时的锁是第二个请求刚加的)。解决办法,加锁时setAbsent(k,v,time)使用给线程生成一个uuid,放到v,释放锁时取v对比是否时此线程家的锁,是才允许删除
  4. 问题3还会有一个问题,比如匹配完是此线加的锁,正要执行删除锁操作,服务器出现了卡顿,锁到时间失效了,请求二开始加锁,加完锁,请求一卡顿结束,开始执行删除锁操作,会将请求二家的锁删除掉,所以要保证判断和删除两个操作的原子性 。解决方法锁续命,开一个子线程判断锁存在,就延长锁的时间,所以只要主线程执行不结束不删除锁,锁永远不会失效。 

    上述问题,可以使用redisson实现,redisson底层封装好了大量的方法,只要调用就可以。(集群环境也有可能不安全?Red lock方案在一定程度上可以解决这个问题,可以设置redis持久化,使用appendsync always :每次有新命令追加到AOF文件时就执行一次fsync,非常慢但安全,但这么样用,不如用zookeeper,效率可能好不如zookeeper)

        如果对性能你要求很高并且可以允许偶尔丢锁,可以选择redis分布式锁;如果要求不丢锁,可以用zookeeper,若使用redlock不要搞主从,持久化时间间隔设置短一些,可能会更严谨

  • 从CAP的角度zookeeper主要满足CP架构 redis集群架构主要满足AP架构  C:一致性 A:可用性 P:分区容错性
  • 618大促的时候,提高性能,除了加机器,还需要优化代码,比如锁(分布式锁、单机锁、进程锁等)一定是先看粒度,一定要把粒度控制的越小越好,能放到锁外的代码,不要放在锁里,非必要不放在锁中。还可以分段加锁优化机制

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值