Skip to content

Commit

Permalink
Redis 缓存库存信息,减少 DB 压力
Browse files Browse the repository at this point in the history
  • Loading branch information
gongfukangEE committed Jun 8, 2019
1 parent 7db7bd1 commit fca2e1e
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 118 deletions.
225 changes: 149 additions & 76 deletions .idea/workspace.xml

Large diffs are not rendered by default.

14 changes: 0 additions & 14 deletions HELP.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ public static Boolean limit() {
return true;
}
} catch (Exception e) {
log.error("获取 Jedis 实例失败:", e);
RedisPool.returnBrokenResource(jedis);
log.error("Limit 获取 Jedis 实例失败:", e);
} finally {
RedisPool.jedisPoolClose(jedis);
}
RedisPool.returnResource(jedis);
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,19 @@ public class RedisKeysConstant {
* 库存
*/
public final static String STOCK = "stock_";

/**
* 库存值
*/
public final static String STOCK_COUNT = "stock_count_";

/**
* 库存值
*/
public final static String STOCK_SALE = "stock_sale_";

/**
* 库存值
*/
public final static String STOCK_VERSION = "stock_version_";
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,21 @@ public class RedisPreheatRunner implements ApplicationRunner {
public void run(ApplicationArguments args) throws Exception {
// 从数据库中查询热卖商品
Stock stock = stockService.getStockById(1);

// 删除 Redis 中旧的缓存
RedisPoolUtil.del(RedisKeysConstant.STOCK + stock.getId());
// 缓存预热
RedisPoolUtil.listPut(RedisKeysConstant.STOCK + stock.getId(), String.valueOf(stock.getCount()),
String.valueOf(stock.getSale()), String.valueOf(stock.getVersion()));

// 删除旧缓存
RedisPoolUtil.del(RedisKeysConstant.STOCK_COUNT + stock.getCount());
RedisPoolUtil.del(RedisKeysConstant.STOCK_SALE + stock.getSale());
RedisPoolUtil.del(RedisKeysConstant.STOCK_VERSION + stock.getVersion());
//缓存预热
int sid = stock.getId();
RedisPoolUtil.set(RedisKeysConstant.STOCK_COUNT + sid, String.valueOf(stock.getCount()));
RedisPoolUtil.set(RedisKeysConstant.STOCK_SALE + sid, String.valueOf(stock.getSale()));
RedisPoolUtil.set(RedisKeysConstant.STOCK_VERSION + sid, String.valueOf(stock.getVersion()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.daydreamdev.secondskill.common.StockWithRedis;

import com.daydreamdev.secondskill.common.RedisKeysConstant;
import com.daydreamdev.secondskill.common.utils.RedisPool;
import com.daydreamdev.secondskill.common.utils.RedisPoolUtil;
import com.daydreamdev.secondskill.pojo.Stock;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

import java.util.List;

/**
* @auther G.Fukang
* @date 6/8 21:47
*/
@Slf4j
public class UpdateStockWithRedis {

/**
* Redis 事务保证库存更新
* 捕获异常后应该删除缓存
*/
public static void updateStockWithRedis(Stock stock) {
Jedis jedis = null;
try {
jedis = RedisPool.getJedis();
// 开始事务
Transaction transaction = jedis.multi();
// 事务操作
RedisPoolUtil.decr(RedisKeysConstant.STOCK_COUNT + stock.getId());
RedisPoolUtil.incr(RedisKeysConstant.STOCK_SALE + stock.getId());
RedisPoolUtil.incr(RedisKeysConstant.STOCK_VERSION + stock.getId());
// 结束事务
List<Object> list = transaction.exec();
} catch (Exception e) {
log.error("updateStock 获取 Jedis 实例失败:", e);
} finally {
RedisPool.jedisPoolClose(jedis);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,6 @@
@Slf4j
public class RedisPoolUtil {

/**
* 判断 key - value 是否存在
*/
public static Boolean exists(String key) {
Jedis jedis = null;
Boolean result = false;

try {
jedis = RedisPool.getJedis();
result = jedis.exists(key);
} catch (Exception e) {
log.error("exists key:{} value:{} error", key, e);
} finally {
RedisPool.jedisPoolClose(jedis);
}
return result;
}

/**
* 设置 key - value 值
*
Expand Down Expand Up @@ -91,6 +73,40 @@ public static Long del(String key) {
return result;
}

/**
* key - value 自增
*/
public static Long incr (String key) {
Jedis jedis = null;
Long result = null;
try {
jedis = RedisPool.getJedis();
result = jedis.incr(key);
} catch (Exception e) {
log.error("listGet key:{} error", key, e);
} finally {
RedisPool.jedisPoolClose(jedis);
}
return result;
}

/**
* key - value 自减
*/
public static Long decr (String key) {
Jedis jedis = null;
Long result = null;
try {
jedis = RedisPool.getJedis();
result = jedis.decr(key);
} catch (Exception e) {
log.error("listGet key:{} error", key, e);
} finally {
RedisPool.jedisPoolClose(jedis);
}
return result;
}

/**
* List - get 操作
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.daydreamdev.secondskill.service.impl;

import com.daydreamdev.secondskill.common.RedisKeysConstant;
import com.daydreamdev.secondskill.common.StockWithRedis.UpdateStockWithRedis;
import com.daydreamdev.secondskill.common.utils.RedisPoolUtil;
import com.daydreamdev.secondskill.dao.StockOrderMapper;
import com.daydreamdev.secondskill.pojo.Stock;
Expand Down Expand Up @@ -63,16 +64,51 @@ public int createOrderWithLimitAndRedis(int sid) throws Exception {
}

/**
* Redis 中校验库存(存在少卖问题)
* 由于更新数据时,从 Redis Stock 删除,因此存在校验时 Redis 数据为 NULL 的情况
* Redis 校验库存
*
* @param sid
*/
private Stock checkStockWithRedis(int sid) throws Exception {
Integer count = Integer.parseInt(RedisPoolUtil.get(RedisKeysConstant.STOCK_COUNT + sid));
Integer sale = Integer.parseInt(RedisPoolUtil.get(RedisKeysConstant.STOCK_SALE + sid));
Integer version = Integer.parseInt(RedisPoolUtil.get(RedisKeysConstant.STOCK_VERSION + sid));
if (count <= 0) {
log.info("库存不足");
throw new RuntimeException("库存不足 Redis currentCount: " + sale);
}
Stock stock = new Stock();
stock.setId(sid);
stock.setCount(count);
stock.setSale(sale);
stock.setVersion(version);
// 此处应该是热更新,但是在数据库中只有一个商品,所以直接赋值
stock.setName("手机");

return stock;
}

/**
* 更新数据库和 DB,保证一致性
*/
private void saleStockOptimsticWithRedis(Stock stock) throws Exception {
int res = stockService.updateStockByOptimistic(stock);
if (res == 0){
throw new RuntimeException("并发更新库存失败") ;
}
// 更新 Redis
UpdateStockWithRedis.updateStockWithRedis(stock);
}


/**
* Redis 中校验库存
* 由于更新数据时,从 Redis Stock 删除,因此存在校验时 Redis 数据为 NULL 的情况
* @param sid
*/
private Stock checkStockWithRedisWithDel(int sid) throws Exception {
Integer count = null;
Integer sale = null;
Integer version = null;
// Boolean res = RedisPoolUtil.exists(RedisKeysConstant.STOCK + sid);
List<String> data = RedisPoolUtil.listGet(RedisKeysConstant.STOCK + sid);
if (data.size() == 0) {
// Redis 不存在,先从数据库中获取,再放到 Redis 中
Expand All @@ -83,9 +119,6 @@ private Stock checkStockWithRedis(int sid) throws Exception {
sale = newStock.getSale();
version = newStock.getVersion();
} else {
if (data.size() == 0 || data.isEmpty()) {
log.error("此处报错**************************");
}
count = Integer.parseInt(data.get(0));
sale = Integer.parseInt(data.get(1));
version = Integer.parseInt(data.get(2));
Expand All @@ -109,7 +142,7 @@ private Stock checkStockWithRedis(int sid) throws Exception {
* 更新数据库和 Redis 库存
* 要保证缓存和 DB 的一致性
*/
private void saleStockOptimsticWithRedis(Stock stock) throws Exception {
private void saleStockOptimsticWithRedisWithDel(Stock stock) throws Exception {
// 乐观锁更新数据库
int res = stockService.updateStockByOptimistic(stock);
// 删除缓存,应该使用 Redis 事务
Expand Down
1 change: 1 addition & 0 deletions target/classes/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ redis.maxIdle=100
redis.maxTotal=300
redis.maxWait=10000
redis.testOnBorrow=true
redis.timeout=100000
# 限流
redis.limit=5

0 comments on commit fca2e1e

Please sign in to comment.