场景业务:一个商品在不同的地区的仓库的库存数量不同,而且会有对应不同批次,不同批次对应不同仓库商品,数量不同。
变量: 商品id , 库存地址 , 批次( 用户来查询时是个区间值 例如: 20210901-202109010 )
示例数据:
商品id | 库存地址 | 批次 | 库存数量 |
1 | 10000 | 20210901 | 100 |
1 | 10000 | 200 |
同一个商品 分为带批次的和不带批次的商品 。
用户下单看到的商品库存有两种查看方式:
1、直接查所有商品的库存数(包含带批次的)
2、商品按 批次 搜索(这个批次是变动的 如:20210901-20210910 或者 20210901-20210980)
那建立缓存的话第一种没啥问题很简单的键值对就可以满足查询的需求。
第二种按批次搜索应该怎么建立缓存的数据结构?
正文:
这里采用的Redis 服务来创建缓存
任何涉及到取值范文的查询第一个想起的存储结构肯定是 Redis 有序集合(sorted set)
下面是需要用到的命令行:
keyName :键名称
语法描述 : ZRANGE keyName [开始score] [结束score] 作用:查询在这个区间的值这个 分数是double类型
示例: ZRANGE keyName 0 20210721
语法描述 : ZADD keyName score value 作用:写入值 值是字符串类型
示例: ZADD keyName 100 -1
语法描述 : ZREM keyName value 作用: 删除对应的值
示例: ZREM keyName -1
注意:这个数据集是set 是不能存储重复数据的
下面说说业务实现逻辑:
1、首先通过 Zadd 将业务数据写入到redis, 批次数据在数据库是不会重复的 可以作为 score 存储
写入缓存部分代码:
// 查询批次商品库存数据 写入缓存
List<Map<String, Object>> findStockPiCiAll = mtlSysItemsSnRepository.findStockPiCiAll();
String keyPrex1 = "pici_stock_on_hand";
String delkeyPici = keyPrex1+"*";
redisService.redisDeleteByPrex(delkeyPici);
findStockPiCiAll.forEach(item -> {
String key = String.format(keyPrex1+"%s_%s", item.get("INVENTORY_ITEM_ID"), item.get("FROM_STORAGE_ADDRESS_CODE"));
Double lot_number = Double.parseDouble(item.get("lot_number").toString());
redisTemplate.opsForZSet().add(key,item.get("AVAILABLE_QTY").toString(), lot_number);
});
2、查询批次通过 ZRANGE 获取一个 String列表 将数据累加起来可以得到对应批次的库存数据
获取批次库存数据部分代码:
/**
* 根据批次信息获取库存
* @param sourceId
* @param deliverCompany 发货方
* @param lot_from1 批次从
* @param lot_to1 批次至
* @return
*/
private Integer getPiCiStock(String sourceId, String deliverCompany,String lot_from1,String lot_to1) {
String keyPrex1 = "pici_stock_on_hand";
// 批次库存缓存名称
String key = String.format(keyPrex1+"%s_%s",sourceId, deliverCompany);
int count = 0;
int lot_from = Integer.parseInt(lot_from1);
int lot_to =Integer.parseInt(lot_to1);
Set redisGetZadd =redisTemplate.opsForZSet().rangeByScore(key, lot_from, lot_to);
if (!CollectionUtils.isEmpty(redisGetZadd)) {
for (Object object : redisGetZadd) {
Double parseDouble = Double.parseDouble(object.toString());
count = count+parseDouble.intValue();
}
}
return count;
}
3、下单业务当订单完成,要对库存做扣减,通过 Zadd 添加一条负数记录(这里要注意的是:下单时用户的购买的商品数量可能相同 但是set 无法插入两条一样的数据,这里需要特殊处理插入前移除相同的主键 如果返回1这说明存在一样的数据 这样新插入的数据就要乘2)
部分代码
// 带批次商品扣减数量
Long quantity = 0-salesBillLine2.getQuantity();
String keyPrex1 = "pici_stock_on_hand";
// 批次库存缓存名称
String key = String.format(keyPrex1+"%s_%s",sourceId, deliverCompany);
Long rangeRemove =redisTemplate.opsForZSet().remove(key, quantity.toString());
if (rangeRemove.intValue()>0) {
quantity = quantity*2;
// 扣减下单库存 加一条负数记录
redisTemplate.opsForZSet().add(key, quantity.toString(), Double.parseDouble(lotNumberFrom));
}else {
// 扣减下单库存 加一条负数记录
redisTemplate.opsForZSet().add(key, quantity.toString(), Double.parseDouble(lotNumberFrom));
}