秒杀场景设计 一

时间:2021-7-3 作者:qvyue

1数据库设计
用户表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

2库存表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for stock
-- ----------------------------
DROP TABLE IF EXISTS `stock`;
CREATE TABLE `stock`  (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '名称',
  `count` int(11) NOT NULL COMMENT '库存',
  `sale` int(11) NOT NULL COMMENT '已售',
  `version` int(11) NOT NULL COMMENT '乐观锁,版本号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

3订单表

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for stock_order
-- ----------------------------
DROP TABLE IF EXISTS `stock_order`;
CREATE TABLE `stock_order`  (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `sid` int(11) NOT NULL COMMENT '库存ID',
  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '商品名称',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

1.秒杀商品写入缓存

 /**
     * 秒杀商品库存缓存写入Reids
     */
    @Test
    public void testLoads() {
        Stock stock = stockDao.selectStock(1);
        redisTemplate.opsForValue().set("kill" + stock.getId(), stock.getId() + "");
    }

2.乐观锁

/**
     * 乐观锁
     */
    @Test
    public void testOptimismLock() {
        Stock stock1 = stockDao.selectStock(1);
        Stock stock2 = stockDao.selectStock(1);
        stockDao.updateSale(stock1);
        int res = stockDao.updateSale(stock2);
        if (res == 0) {
            System.out.println("更新失败");
        }
    }

3.查询缓存

/**
     * 查询缓存
     * 缓存命中,返回缓存;缓存未命中 查询数据库;更新缓存
     */
    @Test
    public void updateCacheCount() {
        int stockId = 1;
        String countKey = stringRedisTemplate.opsForValue().get(CacheKey.STOCK_COUNT + "_" + stockId);
        if (countKey != null) {
            //缓存命中
            System.out.println(countKey);
        } else {
            Stream integerStream = Stream.of(stockDao.selectStock(stockId)).map(e -> {
                return e.getCount() - e.getSale();
            });
            Integer integer = integerStream.findFirst().get();
            Integer count = stockDao.selectStock(1).getCount();
            stringRedisTemplate.opsForValue().set(CacheKey.STOCK_COUNT + "_" + stockId, integer + "", 3600, TimeUnit.SECONDS);
        }
    }

4.用户验证的verifyCode

/**
     * 生成验证Code
     */
    @Test
    public void saveVerifyCode() {
        int stockId = 1;
        int userId = 1;
        String code = stringRedisTemplate.opsForValue().get(CacheKey.HASH_KEY + "_" + stockId);
        User user = userDao.selectByPrimaryKey(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }
        if (code == null) {
            // 生成hash
            String verify = SALT + stockId + userId + "";
            String verifyHash = DigestUtils.md5DigestAsHex(verify.getBytes());
            stringRedisTemplate.opsForValue().set(CacheKey.HASH_KEY + "_" + stockId, verifyHash, 3600, TimeUnit.SECONDS);
        } else {

            System.out.println(code);
        }
    }
  1. 综合

    秒杀场景设计 一
    code-snapshot.png

参考:

Mysql和Redis双写一致性

-先删除缓存,再更新数据库

-先更新数据库,再删缓存

-先删除缓存,再更新数据库,缓存延时双删

接口限流
超卖

-令牌桶限流:使用Guava的RateLimiter实现令牌桶限流接口
-乐观锁

参考:
https://github.com/zitai98/ms

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。