秒杀系统详解

本文探讨了一种高并发秒杀系统的实现,涉及Sentinel限流、数据库加锁与事务处理、分布式ID生成策略,以及Redis分布式锁的挑战与优化。还介绍了Redis预热缓存、Kafka消息队列的应用,以及一致性保证的方法。
摘要由CSDN通过智能技术生成

一、整体架构

1.1项目架构图

秒杀系统架构图

1.2MySQL表格

  • 用户信息表
  • 用户密码表
  • 商品信息表
  • 商品库存表
  • 订单信息表
  • 秒杀信息表(Id;秒杀名称;开始时间;结束时间;秒杀价格;关联商品Id)

二、难点解析

1. 瞬时流量高并发

  • 加入限流措施Sentinel,比如对短时间之内来自某一个用户,某一个IP、某个设备的重复请求做丢弃处理;或者某段时间的请求量超过阈值就限制流量的访问

2. 物品超卖
从Kafka消息中间件中拉取消息后,获取到秒杀商品的product_Id,之后执行事务代码,事务代码中对记录加行锁的语句以及该语句对应的底层SQL如下图所示,加完行锁后再去执行更新库存的逻辑代码,这样其它事务只能等待该事务完成之后才能继续进行操作

productBalanceDao.getLock(product_Id);
 <select id="getLock" resultMap="BaseResultMap" parameterType="java.lang.Long">
        <![CDATA[
            select * from product where id=#{product_Id,jdbcType=BIGINT} for update;
        ]]>
 </select>

3. 分布式环境下的ID生成

  • UUID
  • 雪花算法
    在这里插入图片描述
  • Redis的INCR生成分布式ID
  • Zookeeper的有序节点生成分布式ID

4. Redis分布式锁遇到的一些难点

  • SetNx和Expire的非原子性操作
    – String result = jedis.set(lockKey, requestId, “NX”, “PX”, expireTime);

  • 忘记释放锁
    – finally

  • 释放了别人的锁
    – 将lockKey的值设置为requestId(唯一),在finally中释放锁的时候进行比较当前锁的值是否是自己当时加锁时候的requestId,如果不是就不会释放锁,直接返回

  • 大量失败请求
    – 在规定的时间,比如500毫秒内,自旋不断尝试加锁(说白了,就是在死循环中,不断尝试加锁),如果成功则直接返回。如果失败,则休眠50毫秒,再发起新一轮的尝试。如果到了超时时间,还未加锁成功,则直接返回失败。

  • 如何实现锁重入
    – Redisson框架中实现了可重入锁

  • 锁竞争/锁优化问题
    – a. 读写锁
    – RReadWriteLock readWriteLock = redisson.getReadWriteLock(“readWriteLock”);
    – RLock rLock = readWriteLock.readLock();

    – b. 锁分段 like Concurrentmap
    – 将同一件物品的库存分为多个小库存,可以分段加锁,为了提升系统性能,我们可以将库存分段,比如:分为100段,这样每段就有20个商品可以参与秒杀。在秒杀的过程中,先把用户id获取hash值,然后除以100取模。模为1的用户访问第1段库存,模为2的用户访问第2段库存,模为3的用户访问第3段库存,后面以此类推,到最后模为100的用户访问第100段库存。以前是许多线程竞争一把锁,但现在是多个线程同时竞争100把锁
    在这里插入图片描述

  • 锁超时问题(任务执行时间 > 锁过期时间)
    – a. 锁续命:设置一个后台线程定期查看锁有没有没释放,如果没有被释放的话就给锁续命(使用TimerTask)类实现。在实现自动续期功能时,还需要设置一个总的过期时间,如果业务代码到了这个总的过期时间,还没有执行完,就不再自动续期了。

  • 如何保证缓存和数据库的一致性问题
    – a. 最终一致性:先修改数据库+再删缓存+读写锁优化 ( 修改数据库+删除缓存(写锁) 和 查缓存(空)+读数据库加载到缓存(读锁)------>写和读不能并发执行 ) 、设置缓存的超时时间
    – b. 实时一致性:直接操作DB(放弃缓存、适合读多写多的场景)、canal、分布式数据库

    在这里插入图片描述

  • 针对以上具体问题的解决办法点击这里

三、其它重点

  • Redis作为缓存进行数据预热,如果Redis还有库存,预减Redis库存,并将下单请求加入消息队列
  • Kafka作为消息队列缓存订单请求,它提供了一个异步通信机制,既可以提高并发量,又降低服务之间的耦合度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Malax

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值