订单服务------技术点及亮点2

简介: 订单服务------技术点及亮点2

rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",lockedTo);


package com.saodai.saodaimall.ware.listener;
import com.rabbitmq.client.Channel;
import com.saodai.common.to.OrderTo;
import com.saodai.common.to.mq.StockLockedTo;
import com.saodai.saodaimall.ware.service.WareSkuService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
/**
 * RabbitMQ的监听器
 * 这里有两个监听方法,这两个监听识别的依据是看传入的是StockLockedTo还是OrderTo
 * 一个是监听的库存自动解锁
 * 一个是监听订单取消后库存解锁
 */
@Slf4j
@RabbitListener(queues = "stock.release.stock.queue")
@Service
public class StockReleaseListener {
    @Autowired
    private WareSkuService wareSkuService;
    /**
     ** 监听库存自动解锁
     */
    @RabbitHandler
    public void handleStockLockedRelease(StockLockedTo to, Message message, Channel channel) throws IOException {
        log.info("******收到解锁库存的信息******");
        try {
            System.out.println("******收到解锁库存的信息******");
            //当前消息是否被第二次及以后(重新)派发过来了
            // Boolean redelivered = message.getMessageProperties().getRedelivered();
            //解锁库存
            wareSkuService.unlockStock(to);
            // 手动删除消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            // 解锁失败 将消息重新放回队列,让别人消费
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }
    }
    /**
     *
     * 防止订单服务卡顿,导致订单状态消息一直改不了,库存优先到期,查订单状态新建,什么都不处理
     *导致卡顿的订单,永远都不能解锁库存
     * 订单释放直接和库存释放进行绑定
     * @param orderTo
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitHandler
    public void handleOrderCloseRelease(OrderTo orderTo, Message message, Channel channel) throws IOException {
        log.info("******收到订单关闭,准备解锁库存的信息******");
        try {
            wareSkuService.unlockStock(orderTo);
            // 手动删除消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            // 解锁失败 将消息重新放回队列,让别人消费
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);
        }
    }
}
package com.saodai.common.to.mq;
import lombok.Data;
/**
 * 发送到mq消息队列的to
 **/
@Data
public class StockLockedTo {
    /** 库存工作单的id **/
    private Long id;
    /** 工作单详情的所有信息  StockDetailTo对象内容就是上面的WareOrderTaskDetailEntity **/
    private StockDetailTo detailTo;
}

解锁库存的思路

首先查询数据库的库存详细工作单表看看有没有成功锁定库存(如果成功锁库存了会有对应的一条记录),如果没有那就说明库存没有锁成功,那自然就不需要解锁了


库存详细工作单表有这条记录那就证明库存锁定成功了

具体需不需要解库存还要先看订单状态

先查询有没有这个订单,没有这个订单必须解锁库存(可能出现因为有异常造成的数据回滚导致订单不存在的情况,但是库存锁成功了)

有这个订单,不一定解锁库存,要根据订单的状态来决定是否解库存

订单状态是已取消状态,说明是用户没有支付订单过期了,那就必须解锁库存

订单状态是已支付状态,说明是用户支付成功了,那就不能解锁库存

除了判断上面的情况,还有考虑当前库存详细工作单的状态,只有满足订单状态是已取消状态并且是已锁定的状态那才可以解库存

已锁定:解锁库存

已解锁 :不能再解锁


 /**
     * (这个方法是由StockReleaseListener监听器调用的)
     * 锁库存失败后的自动解锁(也就是回溯)
     * @param to
     */
    @Override
    public void unlockStock(StockLockedTo to) {
        //获取库存详细工作单类
        StockDetailTo detail = to.getDetailTo();
        //库存详细工作单的id
        Long detailId = detail.getId();
    //WareOrderTaskDetailEntity是库存详细工作单类
        WareOrderTaskDetailEntity taskDetailInfo = wareOrderTaskDetailService.getById(detailId);
        if (taskDetailInfo != null) {
            //查出wms_ware_order_task工作单的信息
            Long id = to.getId();
            //订单锁库存工作单(获取哪个订单要锁库存)
            WareOrderTaskEntity orderTaskInfo = wareOrderTaskService.getById(id);
            //获取订单号查询订单状态
            String orderSn = orderTaskInfo.getOrderSn();
            //远程查询订单信息
            R orderData = orderFeignService.getOrderStatus(orderSn);
            if (orderData.getCode() == 0) {
                //订单数据返回成功
                OrderVo orderInfo = orderData.getData("data", new TypeReference<OrderVo>() {});
                /**
                 *     CREATE_NEW(0,"待付款"),
                 *     PAYED(1,"已付款"),
                 *     SENDED(2,"已发货"),
                 *     RECIEVED(3,"已完成"),
                 *     CANCLED(4,"已取消"),
                 *     SERVICING(5,"售后中"),
                 *     SERVICED(6,"售后完成");
                 */
                //订单不存在(因为有异常造成的数据回滚导致订单不存在)或者订单状态是取消状态(orderInfo.getStatus() == 4)才可解库存
                if (orderInfo == null || orderInfo.getStatus() == 4) {
                    //当前库存工作单详情状态1,已锁定,只有当前库存工作单详情状态未解锁才可以解锁
                    if (taskDetailInfo.getLockStatus() == 1) {
                        //调用真正接库存的方法unLockStock
                        unLockStock(detail.getSkuId(),detail.getWareId(),detail.getSkuNum(),detailId);
                    }
                }
            } else {
                //消息拒绝以后重新放在队列里面,让别人继续消费解锁
                //远程调用服务失败
                throw new RuntimeException("远程调用服务失败");
            }
        } else {
            //无需解锁
        }
    }
        /**
     * 真正解锁库存的方法(自动解库存)
     * @param skuId 需要解锁库存的商品id
     * @param wareId  需要解锁库存的库存仓库id
     * @param num  需要解锁库存的商品数量
     * @param taskDetailId   库存工作单详情id
     */
    public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {
        //库存解锁(其实就是修改wms_ware_sku表中的stock_locked的值,之前锁库存锁了多少个就减去多少个)
        wareSkuDao.unLockStock(skuId,wareId,num);
        //更新工作单的状态
        WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
        taskDetailEntity.setId(taskDetailId);
        //setLockStatus(2)表示变为已解锁(1表示已锁定,2表示已解锁,3表示减扣)
        taskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(taskDetailEntity);
    }
    /**
     * 订单取消了就立马解库存
     * 防止订单服务卡顿,导致订单状态消息一直改不了,库存优先到期,查订单状态新建,什么都不处理
     * 导致卡顿的订单,永远都不能解锁库存
     * @param orderTo
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unlockStock(OrderTo orderTo) {
        String orderSn = orderTo.getOrderSn();
        //查一下最新的库存解锁状态,防止重复解锁库存
        WareOrderTaskEntity orderTaskEntity = wareOrderTaskService.getOrderTaskByOrderSn(orderSn);
        //按照工作单的id找到所有 没有解锁的库存,进行解锁(lock_status=1表示已锁定库存)
        Long id = orderTaskEntity.getId();
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                .eq("task_id", id).eq("lock_status", 1));
        for (WareOrderTaskDetailEntity taskDetailEntity : list) {
            //解锁库存
            unLockStock(taskDetailEntity.getSkuId(),
                    taskDetailEntity.getWareId(),
                    taskDetailEntity.getSkuNum(),
                    taskDetailEntity.getId());
        }
    }

自动解库存

/**
     * (这个方法是由StockReleaseListener监听器调用的)
     * 锁库存失败后的自动解锁(也就是回溯)
     * @param to
     */
    @Override
    public void unlockStock(StockLockedTo to) {
        //获取库存详细工作单类
        StockDetailTo detail = to.getDetailTo();
        //库存详细工作单的id
        Long detailId = detail.getId();
    //WareOrderTaskDetailEntity是库存详细工作单类
        WareOrderTaskDetailEntity taskDetailInfo = wareOrderTaskDetailService.getById(detailId);
        if (taskDetailInfo != null) {
            //查出wms_ware_order_task工作单的信息
            Long id = to.getId();
            //订单锁库存工作单(获取哪个订单要锁库存)
            WareOrderTaskEntity orderTaskInfo = wareOrderTaskService.getById(id);
            //获取订单号查询订单状态
            String orderSn = orderTaskInfo.getOrderSn();
            //远程查询订单信息
            R orderData = orderFeignService.getOrderStatus(orderSn);
            if (orderData.getCode() == 0) {
                //订单数据返回成功
                OrderVo orderInfo = orderData.getData("data", new TypeReference<OrderVo>() {});
                /**
                 *     CREATE_NEW(0,"待付款"),
                 *     PAYED(1,"已付款"),
                 *     SENDED(2,"已发货"),
                 *     RECIEVED(3,"已完成"),
                 *     CANCLED(4,"已取消"),
                 *     SERVICING(5,"售后中"),
                 *     SERVICED(6,"售后完成");
                 */
                //订单不存在(因为有异常造成的数据回滚导致订单不存在)或者订单状态是取消状态(orderInfo.getStatus() == 4)才可解库存
                if (orderInfo == null || orderInfo.getStatus() == 4) {
                    //当前库存工作单详情状态1,已锁定,只有当前库存工作单详情状态未解锁才可以解锁
                    if (taskDetailInfo.getLockStatus() == 1) {
                        //调用真正接库存的方法unLockStock
                        unLockStock(detail.getSkuId(),detail.getWareId(),detail.getSkuNum(),detailId);
                    }
                }
            } else {
                //消息拒绝以后重新放在队列里面,让别人继续消费解锁
                //远程调用服务失败
                throw new RuntimeException("远程调用服务失败");
            }
        } else {
            //无需解锁
        }
    }
        /**
     * 真正解锁库存的方法(自动解库存)
     * @param skuId 需要解锁库存的商品id
     * @param wareId  需要解锁库存的库存仓库id
     * @param num  需要解锁库存的商品数量
     * @param taskDetailId   库存工作单详情id
     */
    public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {
        //库存解锁(其实就是修改wms_ware_sku表中的stock_locked的值,之前锁库存锁了多少个就减去多少个)
        wareSkuDao.unLockStock(skuId,wareId,num);
        //更新工作单的状态
        WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
        taskDetailEntity.setId(taskDetailId);
        //setLockStatus(2)表示变为已解锁(1表示已锁定,2表示已解锁,3表示减扣)
        taskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(taskDetailEntity);
    }
  • 自动解库存的具体实现流程
  • 获取库存详细工作单的id
package com.saodai.common.to.mq;
import lombok.Data;
/**
 * 发送到mq消息队列的to
 **/
@Data
public class StockLockedTo {
    /** 库存工作单的id **/
    private Long id;
    /** 工作单详情的所有信息 **/
    private StockDetailTo detailTo;
}
package com.saodai.common.to.mq;
import lombok.Data;
/**
 * 其实就是库存工作单详情实体类(具体给订单的哪个商品锁库存)
 **/
@Data
public class StockDetailTo {
    private Long id;
    /**
     * sku_id
     */
    private Long skuId;
    /**
     * sku_name
     */
    private String skuName;
    /**
     * 购买个数
     */
    private Integer skuNum;
    /**
     * 工作单id
     */
    private Long taskId;
    /**
     * 仓库id
     */
    private Long wareId;
    /**
     * 锁定状态
     */
    private Integer lockStatus;
}
  • 查询数据库有没有这个库存详细工作单类
package com.saodai.saodaimall.ware.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
 * 库存工作单详情(具体给订单的哪个商品锁库存)
 */
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Data
@TableName("wms_ware_order_task_detail")
public class WareOrderTaskDetailEntity implements Serializable {
  private static final long serialVersionUID = 1L;
  /**
   * id
   */
  @TableId
  private Long id;
  /**
   * sku_id
   */
  private Long skuId;
  /**
   * sku_name
   */
  private String skuName;
  /**
   * 购买个数
   */
  private Integer skuNum;
  /**
   * 工作单id
   */
  private Long taskId;
  /**
   * 仓库id
   */
  private Long wareId;
  /**
   * 锁定状态
   */
  private Integer lockStatus;
}
  • 查询订单锁库存工作单(获取哪个订单要锁库存)
package com.saodai.saodaimall.ware.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * 订单锁库存工作单(表示我准备要给哪个订单锁库存了)
 */
@Data
@TableName("wms_ware_order_task")
public class WareOrderTaskEntity implements Serializable {
  private static final long serialVersionUID = 1L;
  /**
   * id
   */
  @TableId
  private Long id;
  /**
   * order_id
   */
  private Long orderId;
  /**
   * order_sn
   */
  private String orderSn;
  /**
   * 收货人
   */
  private String consignee;
  /**
   * 收货人电话
   */
  private String consigneeTel;
  /**
   * 配送地址
   */
  private String deliveryAddress;
  /**
   * 订单备注
   */
  private String orderComment;
  /**
   * 付款方式【 1:在线付款 2:货到付款】
   */
  private Integer paymentWay;
  /**
   * 任务状态
   */
  private Integer taskStatus;
  /**
   * 订单描述
   */
  private String orderBody;
  /**
   * 物流单号
   */
  private String trackingNo;
  /**
   * create_time
   */
  private Date createTime;
  /**
   * 仓库id
   */
  private Long wareId;
  /**
   * 工作单备注
   */
  private String taskComment;
}
  • 根据订单号远程查询订单
package com.saodai.saodaimall.ware.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class OrderVo {
    private Long id;
    /**
     * member_id
     */
    private Long memberId;
    /**
     * 订单号
     */
    private String orderSn;
    /**
     * 使用的优惠券
     */
    private Long couponId;
    /**
     * create_time
     */
    private Date createTime;
    /**
     * 用户名
     */
    private String memberUsername;
    /**
     * 订单总额
     */
    private BigDecimal totalAmount;
    /**
     * 应付总额
     */
    private BigDecimal payAmount;
    /**
     * 运费金额
     */
    private BigDecimal freightAmount;
    /**
     * 促销优化金额(促销价、满减、阶梯价)
     */
    private BigDecimal promotionAmount;
    /**
     * 积分抵扣金额
     */
    private BigDecimal integrationAmount;
    /**
     * 优惠券抵扣金额
     */
    private BigDecimal couponAmount;
    /**
     * 后台调整订单使用的折扣金额
     */
    private BigDecimal discountAmount;
    /**
     * 支付方式【1->支付宝;2->微信;3->银联; 4->货到付款;】
     */
    private Integer payType;
    /**
     * 订单来源[0->PC订单;1->app订单]
     */
    private Integer sourceType;
    /**
     * 订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】
     */
    private Integer status;
    /**
     * 物流公司(配送方式)
     */
    private String deliveryCompany;
    /**
     * 物流单号
     */
    private String deliverySn;
    /**
     * 自动确认时间(天)
     */
    private Integer autoConfirmDay;
    /**
     * 可以获得的积分
     */
    private Integer integration;
    /**
     * 可以获得的成长值
     */
    private Integer growth;
    /**
     * 发票类型[0->不开发票;1->电子发票;2->纸质发票]
     */
    private Integer billType;
    /**
     * 发票抬头
     */
    private String billHeader;
    /**
     * 发票内容
     */
    private String billContent;
    /**
     * 收票人电话
     */
    private String billReceiverPhone;
    /**
     * 收票人邮箱
     */
    private String billReceiverEmail;
    /**
     * 收货人姓名
     */
    private String receiverName;
    /**
     * 收货人电话
     */
    private String receiverPhone;
    /**
     * 收货人邮编
     */
    private String receiverPostCode;
    /**
     * 省份/直辖市
     */
    private String receiverProvince;
    /**
     * 城市
     */
    private String receiverCity;
    /**
     * 区
     */
    private String receiverRegion;
    /**
     * 详细地址
     */
    private String receiverDetailAddress;
    /**
     * 订单备注
     */
    private String note;
    /**
     * 确认收货状态[0->未确认;1->已确认]
     */
    private Integer confirmStatus;
    /**
     * 删除状态【0->未删除;1->已删除】
     */
    private Integer deleteStatus;
    /**
     * 下单时使用的积分
     */
    private Integer useIntegration;
    /**
     * 支付时间
     */
    private Date paymentTime;
    /**
     * 发货时间
     */
    private Date deliveryTime;
    /**
     * 确认收货时间
     */
    private Date receiveTime;
    /**
     * 评价时间
     */
    private Date commentTime;
    /**
     * 修改时间
     */
    private Date modifyTime;
}

进行双重判断

先判断订单不存在(因为有异常造成的数据回滚导致订单不存在)或者订单状态是取消状态

在判断当前库存工作单详情状态是不是1,1表示已锁定,只有当前库存工作单详情状态未解锁才可以解锁

调用unLockStock方法实现真正的解库存(自动解库存)

更新库存的数量(还原)

更新工作单的状态为已解锁

 /**
     * 真正解锁库存的方法(自动解库存)
     * @param skuId 需要解锁库存的商品id
     * @param wareId  需要解锁库存的库存仓库id
     * @param num  需要解锁库存的商品数量
     * @param taskDetailId   库存工作单详情id
     */
    public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {
        //库存解锁(其实就是修改wms_ware_sku表中的stock_locked的值,之前锁库存锁了多少个就减去多少个)
        wareSkuDao.unLockStock(skuId,wareId,num);
        //更新工作单的状态
        WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
        taskDetailEntity.setId(taskDetailId);
        //setLockStatus(2)表示变为已解锁(1表示已锁定,2表示已解锁,3表示减扣)
        taskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(taskDetailEntity);
    }
<!--    解锁库存-->
<update id="unLockStock">
  UPDATE wms_ware_sku
  SET stock_locked = stock_locked - #{num}
  WHERE
  sku_id = ${skuId}
  AND ware_id = #{wareId}
</update>

手动解库存

  • 订单服务的订单取消后立马解库存的具体逻辑
  • 首先通过订单号查询订单锁库存工作单
  • 通过订单锁库存工作单的id去库存详细工作单去找对应的锁库存的记录,看有没有记录并且锁库存的状态是已锁定的状态,防止多次重复解库存(其中库存详细工作单中的工作id的值就是订单锁库存工作单的id的值)
  • 最后调用真正的解库存方法来解库存
   /**
     * 真正解锁库存的方法(自动解库存)
     * @param skuId 需要解锁库存的商品id
     * @param wareId  需要解锁库存的库存仓库id
     * @param num  需要解锁库存的商品数量
     * @param taskDetailId   库存工作单详情id
     */
    public void unLockStock(Long skuId,Long wareId,Integer num,Long taskDetailId) {
        //库存解锁(其实就是修改wms_ware_sku表中的stock_locked的值,之前锁库存锁了多少个就减去多少个)
        wareSkuDao.unLockStock(skuId,wareId,num);
        //更新工作单的状态
        WareOrderTaskDetailEntity taskDetailEntity = new WareOrderTaskDetailEntity();
        taskDetailEntity.setId(taskDetailId);
        //setLockStatus(2)表示变为已解锁(1表示已锁定,2表示已解锁,3表示减扣)
        taskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailService.updateById(taskDetailEntity);
    }
    /**
     * 订单取消了就立马解库存
     * 防止订单服务卡顿,导致订单状态消息一直改不了,库存优先到期,查订单状态新建,什么都不处理
     * 导致卡顿的订单,永远都不能解锁库存
     * @param orderTo
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unlockStock(OrderTo orderTo) {
        String orderSn = orderTo.getOrderSn();
        //查一下最新的库存解锁状态,防止重复解锁库存
        WareOrderTaskEntity orderTaskEntity = wareOrderTaskService.getOrderTaskByOrderSn(orderSn);
        //按照工作单的id找到所有 没有解锁的库存,进行解锁(lock_status=1表示已锁定库存)
        Long id = orderTaskEntity.getId();
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>()
                .eq("task_id", id).eq("lock_status", 1));
        for (WareOrderTaskDetailEntity taskDetailEntity : list) {
            //解锁库存
            unLockStock(taskDetailEntity.getSkuId(),
                    taskDetailEntity.getWareId(),
                    taskDetailEntity.getSkuNum(),
                    taskDetailEntity.getId());
        }
    }


相关实践学习
快速体验阿里云云消息队列RocketMQ版
本实验将带您快速体验使用云消息队列RocketMQ版Serverless系列实例进行获取接入点、创建Topic、创建订阅组、收发消息、查看消息轨迹和仪表盘。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
12月前
|
敏捷开发 监控 数据可视化
看板工具提升产研团队工作效率实操
本文介绍了看板管理在产品研发团队中的应用价值,通过可视化方式呈现任务状态,提高团队协作效率。文章详细解析了五种看板的应用场景,包括Sprint Board、产品迭代计划、用户反馈收集、周报看板及项目整体进度看板,旨在帮助团队更高效地管理和推进项目。
217 1
看板工具提升产研团队工作效率实操
|
数据采集 网络协议 安全
|
JavaScript 前端开发
js中的bind,call,apply方法的区别以及用法
JavaScript中,`bind`、`call`和`apply`均可改变函数的`this`指向并传递参数。其中,`bind`返回一个新函数,不立即执行;`call`和`apply`则立即执行,且`apply`的参数以数组形式传递。三者在改变`this`指向及传参上功能相似,但在执行时机和参数传递方式上有所区别。
215 1
|
存储 分布式计算 安全
MaxCompute Bloomfilter index 在蚂蚁安全溯源场景大规模点查询的最佳实践
MaxCompute 在11月最新版本中全新上线了 Bloomfilter index 能力,针对大规模数据点查场景,支持更细粒度的数据裁剪,减少查询过程中不必要的数据扫描,从而提高整体的查询效率和性能。
|
存储 NoSQL 算法
如何借助Redis更高效统计UV?——Hyperloglog篇
Redis的HyperLogLog数据类型是用于近似计算大规模数据集中不重复元素基数的工具,它以低空间开销(约12KB)提供高精度的估算(误差率约0.81%)。通过`pfadd`添加元素,`pfcount`统计数量,`pfmerge`合并多个HyperLogLog,实现去重计数。尽管内部存储为字符串,但它是概率数据结构,适合高效UV统计和其他大数据场景。
237 0
|
Android开发
Android 对鼠标事件的监听实现
Android 对鼠标事件的监听实现
482 1
|
关系型数据库 MySQL Linux
Linux centos 6.5 - Mysql 安装 、卸载、修改密码、忘记密码 并异常处理
Linux centos 6.5 - Mysql 安装 、卸载、修改密码、忘记密码 并异常处理
480 0
|
人工智能 运维 专有云
阿里云通过信通院多项评估,获评央国企上云服务商“全量领导者”
阿里云飞天企业版凭借“一云多算”能力拿下“可信云技术最佳实践”奖,并通过《面向一云多芯专有云技术能力要求》、《“云+应用”一体化运维能力要求》等多项评估。
561 2
|
监控 Java 调度
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
Java面试题:描述Java线程池的概念、用途及常见的线程池类型。介绍一下Java中的线程池有哪些优缺点
261 1
|
缓存 JavaScript
computed/watch深度监听
computed/watch深度监听
489 1