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<Integer> 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);
}
}
-
综合
code-snapshot.png
参考:
Mysql和Redis双写一致性
-先删除缓存,再更新数据库
-先更新数据库,再删缓存
-先删除缓存,再更新数据库,缓存延时双删
接口限流
超卖
-令牌桶限流:使用Guava的RateLimiter实现令牌桶限流接口
-乐观锁
网友评论