Redis實(shí)戰(zhàn)(3)-數(shù)據(jù)結(jié)構(gòu)List實(shí)戰(zhàn)一之商品信息的有序存儲

作者: 修羅debug
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 by-sa 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。



摘要:電商平臺的管理后端一般有兩大角色的用戶可以使用,一個是系統(tǒng)管理員,一個是平臺的賣家/商家,對于商家而言,管理自個兒的商品是日常工作中再為普通不過的事情了,本文我們將以“有序存儲并展示電商平臺中商家上傳的各式各樣的商品列表”,這里的關(guān)鍵詞是“有序存儲與展示”,我們將借助緩存中間件Redis的數(shù)據(jù)結(jié)構(gòu)~列表List進(jìn)行實(shí)戰(zhàn)實(shí)現(xiàn)!

內(nèi)容:對于Redis的數(shù)據(jù)結(jié)構(gòu)~列表List,在實(shí)際的項(xiàng)目開發(fā)實(shí)戰(zhàn)中,也算是其中一種比較常見、應(yīng)用也比較廣泛的數(shù)據(jù)結(jié)構(gòu)吧!

其底層數(shù)據(jù)存儲結(jié)構(gòu)跟JavaSE集合體系的List有極其相似之處,即數(shù)據(jù)在底層是有序、排好順序的,在將列表的數(shù)據(jù)獲取出來的時候,會發(fā)現(xiàn)其中的數(shù)據(jù)確實(shí)是已經(jīng)排好順序的了,給大家繪制了一個簡單的List的存儲和獲取流程圖,如下所示:


從該圖中,可以看出,當(dāng)我們往Redis的列表List中添加數(shù)據(jù)時,具有“先進(jìn)先出”的特性,即所謂的“FIFO”(有點(diǎn)隊(duì)列Queue的特性?。覕?shù)據(jù)是緊湊、一個挨著一個存儲的!

即當(dāng)我們在往緩存Redis的列表List添加數(shù)據(jù)時,可以采用“LPush 即從左邊的方向添加”的方式往緩存Redis的List中添加,然后再采用“LPop 即從左邊的方向彈出數(shù)據(jù)”或者“RPop 即從右邊的方向彈出數(shù)據(jù)”的方式獲取這一有序存儲的列表數(shù)據(jù)!

知道了列表List的數(shù)據(jù)存儲和讀取流程,其實(shí)我們也就幾乎知曉了在實(shí)際的項(xiàng)目實(shí)戰(zhàn)開發(fā)中的代碼實(shí)現(xiàn)了。

下面我們以“電商平臺~商家添加/下架商品時如何將其商品列表有序存儲至緩存Redis的List中,每次獲取商家當(dāng)前的商品列表時可以直接從緩存中讀取,減少每個商家在每次登陸之后都需要走數(shù)據(jù)庫DB頻繁查詢 所帶來的壓力!”

(1)首先,當(dāng)然是需要來個“產(chǎn)品信息表”啦,其完整的DDL(即數(shù)據(jù)定義語言)如下所示:

CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL COMMENT '商品名稱',
`user_id` int(11) NOT NULL COMMENT '所屬商戶id',
`scan_total` int(255) DEFAULT NULL COMMENT '瀏覽量',
`is_active` tinyint(255) DEFAULT '1' COMMENT '是否有效',
PRIMARY KEY (`id`),
KEY `indx_scan_total` (`scan_total`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商戶商品表';

(2)然后,當(dāng)然是需要開發(fā)一個Controller啦,在該Controller中我們需要開設(shè)兩個請求方法,一個是給商戶使用的“添加商品”(進(jìn)DB,并塞入Redis),另外一個是獲取當(dāng)前商戶已添加的“商品列表”,其完整的源代碼如下所示:  

/**
* 列表List實(shí)戰(zhàn)-商戶商品列表存儲
* @Author:debug (SteadyJack)
* @Link: weixin-> debug0868 qq-> 1948831260
* @Date: 2019/10/30 20:58
**/
@RestController
@RequestMapping("list")
public class ListController extends AbstractController{

@Autowired
private ListService listService;

//添加
@RequestMapping(value = "put",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse put(@RequestBody Product product,BindingResult result){
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
log.info("--商戶商品信息:{}",product);
response.setData(listService.addProduct(product));
}catch (Exception e){
log.error("--List實(shí)戰(zhàn)-商戶商品-添加-發(fā)生異常:",e.fillInStackTrace());
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}

//獲取列表詳情
@RequestMapping(value = "get",method = RequestMethod.GET)
public BaseResponse get(@RequestParam("userId") final Integer userId){
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
response.setData(listService.getHistoryProducts(userId));

}catch (Exception e){
log.error("--List實(shí)戰(zhàn)-商戶商品-獲取列表-發(fā)生異常:",e.fillInStackTrace());
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
}

(3)緊接著,我們需要開發(fā)Controller對應(yīng)的Service,其職責(zé)當(dāng)然是用來處理真正的業(yè)務(wù)邏輯,“添加商品”時,它負(fù)責(zé)將該商品信息添加進(jìn)DB數(shù)據(jù)庫,并添加進(jìn)緩存Redis中;“獲取商品”時,自然而然是從緩存Redis獲取出List列表數(shù)據(jù),其完整的源代碼如下所示:  

/**
* 列表List服務(wù)
* @Author:debug (SteadyJack)
* @Link: weixin-> debug0868 qq-> 1948831260
* @Date: 2019/10/30 9:48
**/
@Service
public class ListService {

public static final Logger log= LoggerFactory.getLogger(ListService.class);

@Autowired
private ProductMapper productMapper;

@Autowired
private RedisTemplate redisTemplate;

//添加商品
@Transactional(rollbackFor = Exception.class)
public Integer addProduct(Product product) throws Exception{
if (product!=null){
product.setId(null);
//將該商品塞入數(shù)據(jù)庫DB中
productMapper.insertSelective(product);
Integer id=product.getId();

if (id>0){
//將該商品塞入緩存Redis中
this.pushRedisService(product);
}
return id;
}
return -1;
}

//TODO:往緩存中塞信息-可以抽取到ListRedisService
private void pushRedisService(final Product product) throws Exception{
ListOperations<String,Product> listOperations=redisTemplate.opsForList();
listOperations.leftPush(Constant.RedisListPrefix+product.getUserId(),product);
}

//獲取歷史發(fā)布的商品列表
public List<Product> getHistoryProducts(final Integer userId) throws Exception{
List<Product> list= Lists.newLinkedList();

ListOperations<String,Product> listOperations=redisTemplate.opsForList();
final String key=Constant.RedisListPrefix+userId;
//TODO:倒序->userID=10010 ->Rabbitmq入門與實(shí)戰(zhàn),Redis入門與實(shí)戰(zhàn),SpringBoot項(xiàng)目實(shí)戰(zhàn)
list=listOperations.range(key,0,listOperations.size(key));
log.info("--倒序:{}",list);

//TODO:順序->userID=10010 ->SpringBoot項(xiàng)目實(shí)戰(zhàn),Redis入門與實(shí)戰(zhàn),Rabbitmq入門與實(shí)戰(zhàn)
//Collections.reverse(list);
//log.info("--順序:{}",list);

//TODO:彈出來移除的方式
//Product entity=listOperations.rightPop(key);
//while (entity!=null){
//list.add(entity);
//entity=listOperations.rightPop(key);
//}
return list;
}
}

(4)至此,我們的代碼實(shí)戰(zhàn)就完畢了,最后我們就基于Postman進(jìn)入測試環(huán)節(jié)吧,我們給商戶10010添加兩個商品吧,如下所示:



完成之后還可以前往數(shù)據(jù)庫Mysql中查看剛剛已經(jīng)添加完成的商品列表,如下圖所示:


最后,我們再在Postman發(fā)起“獲取當(dāng)前商戶已經(jīng)添加完成的商品列表-有序展示”(可以通過修改代碼的方式,實(shí)現(xiàn)“順序”、“倒序”等功效),如下所示:


好了,本篇文章我們就介紹到這里了,建議各位小伙伴一定要照著文章提供的樣例代碼擼一擼,只有擼過才能知道這玩意是咋用的,否則就成了“空談?wù)摺保?/span>

對Redis相關(guān)技術(shù)棧以及實(shí)際應(yīng)用場景實(shí)戰(zhàn)感興趣的小伙伴可以前往Debug搭建的技術(shù)社區(qū)的課程中心進(jìn)行學(xué)習(xí)觀看:https://www.fightjava.com/web/index/course/detail/12 !

其他相關(guān)的技術(shù),感興趣的小伙伴可以關(guān)注底部Debug的技術(shù)公眾號,或者加Debug的微信,拉你進(jìn)“微信版”的真正技術(shù)交流群!一起學(xué)習(xí)、共同成長!

補(bǔ)充:

1、本文涉及到的相關(guān)的源代碼可以到此地址,check出來進(jìn)行查看學(xué)習(xí):

https://gitee.com/steadyjack/SpringBootRedis

2、目前Debug已將本文所涉及的內(nèi)容整理錄制成視頻教程,感興趣的小伙伴可以前往觀看學(xué)習(xí):https://www.fightjava.com/web/index/course/detail/12

3、關(guān)注一下Debug的技術(shù)微信公眾號,最新的技術(shù)文章、課程以及技術(shù)專欄將會第一時間在公眾號發(fā)布哦!