articleList

08-分布缓存实战-List-Hash 数据结构最佳案例实战

2025/03/16 posted in  Redis
Tags: 

第 1 集 在线教育-天热销视频榜单实战-List 数据结构设计

简介:在线教育-天热销视频榜单实战-List 数据结构设计

  • 需求

    • 小滴课堂官网需要一个视频学习榜单,每天更新一次
    • 需要支持人工运营替换榜单位置
  • 企业中流程

    • 定时任务计算昨天最多人学习的视频
    • 晚上 12 点到 1 点更新到榜单上
    • 预留一个接口,支持人工运营
  • 类似场景

    • 京东:热销手机榜单、电脑榜单等
    • 百度:搜索热榜
  • 疑惑:为啥不是实时计算,真正高并发下项目,都是预先计算好结果,然后直接返回数据,且存储结构最简单

第 2 集 在线教育-天热销视频榜单实战-编码最佳实践

简介:在线教育-天热销视频榜单实战-编码最佳实践

  • 开发接口

    @RequestMapping("rank")
    public JsonData videoRank(){
    ​
        List<VideoDO> list = redisTemplate.opsForList().range(RANK_KEY,0,-1);
        return JsonData.buildSuccess(list);
    }
    
  • 测试数据

    @Test
    void saveRank(){
          String RANK_KEY = "rank:video";
          VideoDO video1 = new VideoDO(3,"PaaS工业级微服务大课","xdclass.net",1099);
          VideoDO video2 = new VideoDO(5,"AlibabaCloud全家桶实战","xdclass.net",59);
          VideoDO video3 = new VideoDO(53,"SpringBoot2.X+Vue3综合实战","xdclass.net",49);
          VideoDO video4 = new VideoDO(15,"玩转23种设计模式+最近实战","xdclass.net",49);
          VideoDO video5 = new VideoDO(45,"Nginx网关+LVS+KeepAlive","xdclass.net",89);
          redisTemplate.opsForList().leftPushAll(RANK_KEY,video4,video5,video3,video2,video1);
      }
    
  • 人工运营操作榜单

    /**
     * 替换榜单第二名
      */
    @Test
    void replaceRank(){
        String RANK_KEY = "rank:video";
        VideoDO video = new VideoDO(42,"小滴课堂面试专题第一季高级工程师","xdclass.net",89);
        //在集合的指定位置插入元素,如果指定位置已有元素,则覆盖,没有则新增
        redisTemplate.opsForList().set(RANK_KEY,1,video);
    }
    

第 3 集 自营电商平台-购物车实现案例-Hash 数据结构最佳实践

简介:自营电商平台-购物车实现案例-Hash 数据结构最佳实践

  • 背景

    • 电商购物车实现,支持买多件商品,每个商品可以买不同数量
    • 支持高性能处理
  • 购物车常见实现方式

    • 实现方式一:存储到数据库

      • 性能存在瓶颈
    • 实现方式二:前端本地存储-localstorage-sessionstorage

      • localstorage 在浏览器中存储 key/value 对,没有过期时间。
      • sessionstorage 在浏览器中存储 key/value 对,在关闭会话窗口后将会删除这些数据。
    • 实现方式三:后端存储到缓存如 redis

      • 可以开启 AOF 持久化防止重启丢失(推荐)
  • 购物车数据结构介绍

    • 一个购物车里面,存在多个购物项

    • 所以 购物车结构是一个双层 Map:

      • Map<String,Map<String,String>>
      • 第一层 Map,Key 是用户 id
      • 第二层 Map,Key 是购物车中商品 id,值是购物车数据
  • 对应 redis 里面的存储

    • redis 里面有多种数据结构,应该使用哪种?
    • 答案是 hash 结构

第 4 集 高并发下的互联网电商购物车实战-相关 VO 类和数据准备

简介:高并发下的互联网电商购物车实战-相关 VO 类和数据准备

  • VideoDO 类

  • CartItemVO

    public class CartItemVO {
    ​
        /**
        * 商品id
        */
        private Integer productId;
    ​
        /**
        * 购买数量
        */
        private Integer buyNum;
    ​
        /**
        * 商品标题
        */
        private String productTitle;
    ​
        /**
        * 图片
        */
        private String productImg;
    ​
        /**
        * 商品单价
        */
        private int price ;
    ​
        /**
        * 总价格,单价+数量
        */
        private int totalPrice;
    ​
    ​
        public int getProductId() {
            return productId;
        }
    ​
        public void setProductId(int productId) {
            this.productId = productId;
        }
    ​
        public Integer getBuyNum() {
            return buyNum;
        }
    ​
        public void setBuyNum(Integer buyNum) {
            this.buyNum = buyNum;
        }
    ​
        public String getProductTitle() {
            return productTitle;
        }
    ​
        public void setProductTitle(String productTitle) {
            this.productTitle = productTitle;
        }
    ​
        public String getProductImg() {
            return productImg;
        }
    ​
        public void setProductImg(String productImg) {
            this.productImg = productImg;
        }
    ​
        /**
        * 商品单价 * 购买数量
        * @return
        */
        public int getTotalPrice() {
    ​
            return this.price*this.buyNum;
        }
    ​
        public int getPrice() {
            return price;
        }
    ​
        public void setPrice(int price) {
            this.price = price;
        }
    ​
        public void setTotalPrice(int totalPrice) {
            this.totalPrice = totalPrice;
        }
    }
    
  • CartIVO

    public class CartVO {
    ​
        /**
        * 购物项
        */
        private List<CartItemVO> cartItems;
    ​
    ​
        /**
        * 购物车总价格
        */
        private Integer totalAmount;
    ​
    ​
    ​
        /**
        * 总价格
        * @return
        */
        public int getTotalAmount() {
            return cartItems.stream().mapToInt(CartItemVO::getTotalPrice).sum();
        }
    ​
    ​
    ​
        public List<CartItemVO> getCartItems() {
            return cartItems;
        }
    ​
        public void setCartItems(List<CartItemVO> cartItems) {
            this.cartItems = cartItems;
        }
    }
    
  • 数据源层

    @Repository
    public class VideoDao {
    ​
        private static Map<Integer,VideoDO> map = new HashMap<>();
    ​
        static {
            map.put(1,new VideoDO(1,"工业级PaaS云平台+SpringCloudAlibaba 综合项目实战(完结)","https://xdclass.net",1099));
            map.put(2,new VideoDO(2,"玩转新版高性能RabbitMQ容器化分布式集群实战","https://xdclass.net",79));
            map.put(3,new VideoDO(3,"新版后端提效神器MybatisPlus+SwaggerUI3.X+Lombok","https://xdclass.net",49));
            map.put(4,new VideoDO(4,"玩转Nginx分布式架构实战教程 零基础到高级","https://xdclass.net",49));
            map.put(5,new VideoDO(5,"ssm新版SpringBoot2.3/spring5/mybatis3","https://xdclass.net",49));
            map.put(6,new VideoDO(6,"新一代微服务全家桶AlibabaCloud+SpringCloud实战","https://xdclass.net",59));
        }
    ​
    ​
        /**
        * 模拟从数据库找
        * @param videoId
        * @return
        */
        public VideoDO findDetailById(int videoId) {
            return map.get(videoId);
        }
    }
    
  • json 工具类

    public class JsonUtil {
    ​
        // 定义jackson对象
        private static final ObjectMapper MAPPER = new ObjectMapper();
    ​
        /**
        * 将对象转换成json字符串。
    
        * @return
        */
        public static String objectToJson(Object data) {
            try {
                String string = MAPPER.writeValueAsString(data);
                return string;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    ​
        /**
        * 将json结果集转化为对象
        *
        * @param jsonData json数据
        * @param clazz 对象中的object类型
        * @return
        */
        public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
            try {
                T t = MAPPER.readValue(jsonData, beanType);
                return t;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    ​
    }
    

第 5 集 高并发下的互联网电商购物车实战-加入购物车接口开发

简介:电商购物车实现案例-加入购物车接口开发

  • 添加购物车接口

    @RequestMapping("addCart")
    public JsonData addCart(int videoId,int buyNum){
    ​
        //获取购物车
        BoundHashOperations<String, Object, Object> myCart = getMyCartOps();
    ​
        Object cacheObj = myCart.get(videoId+"");
        String result = "";
    ​
        if (cacheObj != null) {
            result = (String) cacheObj;
        }
        if (cacheObj == null) {
            //不存在则新建一个购物项
            CartItemVO cartItem = new CartItemVO();
            //从数据库查询详情,我们这边直接随机写个
    ​
            VideoDO videoDO = videoDao.findDetailById(videoId);
            videoDO.setId(videoId);
    ​
            cartItem.setPrice(videoDO.getPrice());
            cartItem.setBuyNum(buyNum);
            cartItem.setProductId(videoId);
            cartItem.setProductImg(videoDO.getImg());
            cartItem.setProductTitle(videoDO.getTitle());
            myCart.put(videoId+"", JsonUtil.objectToJson(cartItem));
    ​
        } else {
    ​
            //存在则新增数量
            CartItemVO cartItem = JsonUtil.jsonToPojo(result, CartItemVO.class);
            cartItem.setBuyNum(cartItem.getBuyNum() + buyNum);
            myCart.put(videoId+"", JsonUtil.objectToJson(cartItem));
    ​
        }
    ​
        return JsonData.buildSuccess();
    ​
    }
    
  • 购物车方法抽取

    /**
      * 抽取我的购物车通用方法
      *
      * @return
      */
    private BoundHashOperations<String, Object, Object> getMyCartOps() {
        String cartKey = getCartKey();
        return redisTemplate.boundHashOps(cartKey);
    }
    ​
    ​
    /**
      * 获取购物车的key
      *
      * @return
      */
    private String getCartKey() {
        //从拦截器获取 ,这里写死即可,每个用户不一样
        int userId = 88;
        String cartKey = String.format("product:cart:%s", userId);
        return cartKey;
    }
    

第 6 集 高并发下的互联网电商购物车实战-查看和清空购物车功能开发

简介:高并发下的互联网电商购物车实战-查看和清空购物车功能开发

  • 查看我的购物车

    @GetMapping("/mycart")
    public JsonData findMyCart(){
    ​
        BoundHashOperations<String,Object,Object> myCart = getMyCartOps();
    ​
        List<Object> itemList = myCart.values();
    ​
        List<CartItemVO> cartItemVOList = new ArrayList<>();
    ​
        for(Object item: itemList){
            CartItemVO cartItemVO = JsonUtil.jsonToPojo((String)item,CartItemVO.class);
            cartItemVOList.add(cartItemVO);
        }
    ​
        //封装成cartvo
        CartVO cartVO = new CartVO();
        cartVO.setCartItems(cartItemVOList);
    ​
    ​
        return JsonData.buildSuccess(cartVO);
    }
    
  • 清空购物车

    @GetMapping("/clear")
    public JsonData clear() {
            String cartKey = getCartKey();
            redisTemplate.delete(cartKey);
            return JsonData.buildSuccess();
    }