• 【微服务】Day14(续 秒杀业务准备、消息队列)


    秒杀业务准备

    开发根据SpuId查询秒杀Sku列表信息

    我们将秒杀的商品Spu列表查询出来

    当用户选择一个商品时

    我们要将这个商品的sku也查询出来

    也就是根据SpuId查询Sku的列表

    创建SeckillSkuMapper

    @Repository
    public interface SeckillSkuMapper {
       
    
        // 根据spuId查询sku列表
        List<SeckillSku> findSeckillSkusBySpuId(Long spuId);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    SeckillSkuMapper.xml文件添加内容

    <sql id="SimpleField">
        <if test="true">
            id,
            sku_id,
            spu_id,
            seckill_stock,
            seckill_price,
            gmt_create,
            gmt_modified,
            seckill_limit
        if>
    sql>
    <select id="findSeckillSkusBySpuId" resultMap="BaseResultMap">
        select
            <include refid="SimpleField" />
        from
            seckill_sku
        where
            spu_id=#{spuId}
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    根据当前时间查询正在进行秒杀的商品

    根据给定时间查询出正在进行秒杀的商品列表

    在秒杀过程中,一定会将当前时间正在进行秒杀商品查询出来的

    首先保证数据库中的seckill_spu表的数据正在秒杀时间段(检查数据,如果不在秒杀时间段,将结束时间后移如2024年)

    SeckillSpuMapper添加方法

    // 根据指定时间,查询正在进行秒杀的商品
    List<SeckillSpu> findSeckillSpusByTime(LocalDateTime time);
    
    • 1
    • 2

    SeckillSpuMapper.xml

    
    <select id="findSeckillSpusByTime" resultMap="BaseResultMap">
        select
            <include refid="SimpleField" />
        from
            seckill_spu
        where
            start_time < #{time}
        and
            end_time > #{time}
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    根据SpuId查询秒杀商品详情

    SeckillSpuMapper接口添加方法

    // 根据SpuId查询spu秒杀信息
    SeckillSpu findSeckillSpuById(Long spuId);
    
    • 1
    • 2

    SeckillSpuMapper.xml添加内容

    
    <select id="findSeckillSpuById" resultMap="BaseResultMap">
        select
            <include refid="SimpleField" />
        from
            seckill_spu
        where
            spu_id=#{spuId}
    select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    查询所有秒杀商品的SpuId

    这个查询需要是为了后面布隆过滤器加载数据库中包含的所有SpuId时使用

    因为布隆过滤器的特性,只需要查询出所有商品的spu_id即可

    SeckillSpuMapper接口添加方法

    // 布隆过滤器用:查询获得所有秒杀商品的SpuId数组
    Long[] findAllSeckillSpuIds();
    
    • 1
    • 2

    SeckillSpuMapper.xml添加内容

    
    <select id="findAllSeckillSpuIds" resultType="long" >
        select spu_id from seckill_spu
    select>
    
    • 1
    • 2
    • 3
    • 4

    缓存预热思路

    在即将发生高并发业务之前,我们将一些高并发业务中需要的数据保存到Redis中,这种操作,就是"缓存预热",这样发生高并发时,这些数据就可以直接从Redis中获得,无需查询数据库了

    我们要利用Quartz周期性的将每个批次的秒杀商品,预热到Redis

    例如每天的12:00 14:00 16:00 18:00进行秒杀

    那么就在 11:55 13:55 15:55 17:55 进行预热

    我们预热的内容有

    1. 我们预热的内容是将参与秒杀商品的sku查询出来,根据skuid将该商品的库存保存在Redis中

      还要注意为了预防雪崩,在向Redis保存数据时,都应该添加随机数

    2. (待完善).在秒杀开始前,生成布隆过滤器,访问时先判断布隆过滤器,如果判断商品存在,再继续访问

    3. 在秒杀开始之前,生成每个商品对应的随机码,保存在Redis中,随机码可以绑定给有Spu,防止有用户知道SpuId暴力访问

    设置定时任务

    将库存和随机码保存到Redis

    利用Quartz将库存数和随机码保存到Redis

    1.创建Job接口实现类

    2.创建配置类,配置JobDetail和Trigger

    在seckill包下创建timer.job包

    在seckill包下创建timer.config包

    首先我们编写缓存预热的操作,在job包下创建类SeckillInitialJob

    @Slf4j
    public class SeckillInitialJob implements Job {
       
    
        // 查询秒杀sku库存数的mapper
        @Autowired
        private SeckillSkuMapper skuMapper;
        // 需要查询秒杀spu相关的信息
        @Autowired
        private SeckillSpuMapper spuMapper;
        // 操作Redis的对象
        @Autowired
        private RedisTemplate redisTemplate;
        /*
        RedisTemplate对象在保存数据到Redis时,会先将当前数据序列化后保存
        这样做的好处是序列化后的数据保存到Redis读写效率更高,缺点是不能在Redis中修改数据
        我们现在要预热的是秒杀sku库存数,这个库存数如果也用上面的redisTemplate保存的方式
        就容易在高并发情况下由于线程安全问题导致"超卖"
        解决方案就是我们需要创建一个能够直接在Redis中修改数据的对象,来避免超卖的发生
        SpringDataRedis提供了一个可以直接在Redis中操作数值的对象:StringRedisTemplate
        使用StringRedisTemplate向Redis保存数据,默认都会以字符串的方式保存(不序列化了)
        又因为Redis支持直接将字符串类型的数值进行修改,所以可以将库存数保存并直接修改
        这样就不需要写java代码读取和修改了,直接使用Redis内部的功能修改库存数
        最后结合Redis天生单线程的特性,避免线程安全问题方式超卖
         */
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        @Override
        public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
       
            // 当前方法是缓存预热操作
            // 它执行的时间是秒杀开始前5分钟,所以获取一个5分钟后的时间对象
            LocalDateTime time=LocalDateTime
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    EMQX数据流转MySQL踩坑日记:EMQX VER 4.2.3
    [Spring Framework]注解开发③(依赖注入)
    二、vue基础入门
    服务器相关
    哈希散列表hlist_head - linux内核经典实例
    2020 MIT6.s081 Lab Locks
    微前端一:技术选型
    Python实现Redis缓存MySQL数据并支持数据同步
    Mac安装虚拟机和CentOS
    套接字Socket编程实践(C语言版)
  • 原文地址:https://blog.csdn.net/shortcutsuccess/article/details/127430478