秒杀高并发解决方案
秒杀高并发解决方案
1.秒杀/高并发方案-介绍
- 秒杀/高并发 其实主要解决两个问题,一个是并发读,一个是并发写
- 并发读的核心优化理念是尽量减少用户到 DB 来"读"数据,或者让他们读更少的数据, 并
发写的处理原则也一样 - 针对秒杀系统需要做一些保护,针对意料之外的情况设计兜底方案,以防止最坏的情况
发生。 - 系统架构要满足高可用: 流量符合预期时要稳定,要保证秒杀活动顺利完成,即秒杀商品
顺利地卖出去,这个是最基本的前提 - 系统保证数据的一致性: 就是秒杀 10 个 商品 ,那就只能成交 10 个商品,多一个少一
个都不行。一旦库存不对,就要承担损失 - 系统要满足高性能: 也就是系统的性能要足够高,需要支撑大流量, 不光是服务端要做极
致的性能优化,而且在整个请求链路上都要做协同的优化,每个地方快一点, 整个系统就"快
"了 - 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键, 对应的方案比如页
面缓存方案、Redis 预减库存/内存标记与隔离、请求的削峰(RabbitMQ/异步请求)、分布式
Session 共享等


2.秒杀场景模拟



这里模拟每秒1000个请求,循环10次即总共发起10000次请求,请求/seckill/doSeckill接口
3.秒杀解决方案
3.1 v1.0-原始版本
SeckillController
@Controller
@RequestMapping(value = "/seckill")
public class SeckillController {@Resourceprivate GoodsService goodsService;@Resourceprivate SeckillOrderService seckillOrderService;@Resourceprivate OrderService orderService;@RequestMapping(value = "/doSeckill")public String doSeckill(Model model, User user, Long goodsId) {System.out.println("-----秒杀 V1.0--------");//===================秒杀 v1.0 start =========================if (user == null) {return "login";}model.addAttribute("user", user);GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);//判断库存if (goodsVo.getStockCount() < 1) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}//解决重复抢购LambdaQueryWrapper<SeckillOrder> seckillOrderLambdaQueryWrapper = new QueryWrapper<SeckillOrder>().lambda().eq(SeckillOrder::getGoodsId, goodsId).eq(SeckillOrder::getUserId, user.getId());SeckillOrder seckillOrder = seckillOrderService.getOne(seckillOrderLambdaQueryWrapper);if (seckillOrder != null) {model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}//抢购Order order = orderService.seckill(user, goodsVo);if (order == null) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}model.addAttribute("order", order);model.addAttribute("goods", goodsVo);return "orderDetail";//===================秒杀 v1.0 end... =========================}}
OrderServiceImpl
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {@Resourceprivate SecKillGoodsService secKillGoodsService;@Resourceprivate OrderMapper orderMapper;@Resourceprivate SeckillOrderMapper seckillOrderMapper;/*** 秒杀商品,减少库存* @param user* @param goodsVo* @return*/@Overridepublic Order seckill(User user, GoodsVo goodsVo) {//1.根据商品id获取秒杀商品信息LambdaQueryWrapper<SecKillGoods> lambdaQueryWrapper = new QueryWrapper<SecKillGoods>().lambda().eq(SecKillGoods::getGoodsId,goodsVo.getId());SecKillGoods seckillGoods = secKillGoodsService.getOne(lambdaQueryWrapper);//2.秒杀商品库存-1seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);secKillGoodsService.updateById(seckillGoods);//3.生成普通订单Order order = new Order();order.setUserId(user.getId());order.setGoodsId(goodsVo.getId());order.setDeliveryAddrId(0L);order.setGoodsName(goodsVo.getGoodsName());order.setGoodsCount(1);order.setGoodsPrice(seckillGoods.getSeckillPrice());order.setOrderChannel(1);order.setStatus(0);order.setCreateDate(new Date());orderMapper.insert(order);//4.生成秒杀订单SeckillOrder seckillOrder = new SeckillOrder();seckillOrder.setGoodsId(goodsVo.getId());seckillOrder.setUserId(user.getId());seckillOrder.setOrderId(order.getId());seckillOrderMapper.insert(seckillOrder);return order;}
}
说明
非高并发情况下,程序执行流程分析
-
程序首先校验用户是否登录
-
然后判断商品库存是充足
-
在判断是否存在重复抢购
-
最后执行抢购生成订单及抢购订单数据
高并发情况下,程序执行流程分析
- 判断商品库存是充足、否存在重复抢购的程序是不具备原子性
- 秒杀商品的库存为负数,但具体的值无法确定,因为获取秒杀商品库存也不具备原子性
- 当有多少个请求冲破了前面库存和抢购的过滤,就会去生成多少个订单
3.2 v2.0-秒杀-复购、超卖处理
SeckillController
@RequestMapping(value = "/doSeckill")
public String doSeckill(Model model, User user, Long goodsId) {System.out.println("-----秒杀 V2.0--------");//===================秒杀 v2.0 start =========================if (user == null) {return "login";}model.addAttribute("user", user);GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);//判断库存if (goodsVo.getStockCount() < 1) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}//解决重复抢购,直接到redis中获取对应的秒杀订单,如果有则说明已经抢购了,不能继续抢购SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);if (seckillOrder != null) {model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}//抢购Order order = orderService.seckill(user, goodsVo);if (order == null) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}model.addAttribute("order", order);model.addAttribute("goods", goodsVo);return "orderDetail";//===================秒杀 v2.0 end... =========================
}
OrderServiceImpl
/*** 秒杀商品,减少库存** @param user* @param goodsVo* @return*/
@Override
@Transactional(rollbackFor = {Exception.class})
public Order seckill(User user, GoodsVo goodsVo) {//1.根据商品id获取秒杀商品信息LambdaQueryWrapper<SecKillGoods> lambdaQueryWrapper = new QueryWrapper<SecKillGoods>().lambda().eq(SecKillGoods::getGoodsId, goodsVo.getId());SecKillGoods seckillGoods = secKillGoodsService.getOne(lambdaQueryWrapper);//2.秒杀商品库存-1// seckillGoods.setStockCount(seckillGoods.getStockCount() - 1);// secKillGoodsService.updateById(seckillGoods);//1. Mysql在默认的事务隔离级别[REPEATABLE-READ]下//2. 执行update语句时,会在事务中锁定要更新的行//3. 这样可以防止其它会话在同一行执行update,deleteLambdaUpdateWrapper<SecKillGoods> updateWrapper = new UpdateWrapper<SecKillGoods>().lambda().setSql("stock_count=stock_count-1").eq(SecKillGoods::getGoodsId, goodsVo.getId()).gt(SecKillGoods::getStockCount, 0);boolean update = secKillGoodsService.update(updateWrapper);//如果更新失败说明已经没有库存了if (!update) {return null;}//3.生成普通订单Order order = new Order();order.setUserId(user.getId());order.setGoodsId(goodsVo.getId());order.setDeliveryAddrId(0L);order.setGoodsName(goodsVo.getGoodsName());order.setGoodsCount(1);order.setGoodsPrice(seckillGoods.getSeckillPrice());order.setOrderChannel(1);order.setStatus(0);order.setCreateDate(new Date());orderMapper.insert(order);//4.生成秒杀订单SeckillOrder seckillOrder = new SeckillOrder();seckillOrder.setGoodsId(goodsVo.getId());seckillOrder.setUserId(user.getId());seckillOrder.setOrderId(order.getId());seckillOrderMapper.insert(seckillOrder);//设置秒杀订单的key --> order:用户id:商品idredisTemplate.opsForValue().set("order:" + user.getId() + ":" + goodsVo.getId(), seckillOrder);return order;
}
说明
超卖问题处理前面出现超卖的主要原因在于,更新库存时不具备原子性,这里利用mysql默认的事务隔离级别
[REPEATABLE-READ]在事务中执行update语句获取锁定更新的行,这个机制进行处理
复购(生成了多个订单)问题处理,则在生成订单之前对库存更新进行判断,如果更新失败则不在生成订单
//如果更新失败说明已经没有库存了if (!update) {return null;}
优化复购判断在生成秒杀订单之后将订单存储到redis中,在判断复购时直接从redis中获取,避免频繁查询数据库,提升执行效率
//解决重复抢购,直接到redis中获取对应的秒杀订单,如果有则说明已经抢购了,不能继续抢购SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);if (seckillOrder != null) {model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}
测试结果


3.3 v3.0 优化秒杀,Redis 预减库存
- 前面我们防止超卖 是通过到数据库查询和到数据库抢购,来完成的, 代码如下:


- 如果在短时间内,大量抢购冲击 DB, 造成洪峰, 容易压垮数据库
- 解决方案: 使用 Redis 完成预减库存,如果没有库存了, 直接返回, 减小对 DB 的压力
- 示意图:

代码实现
@Controller
@RequestMapping(value = "/seckill")
public class SeckillController implements InitializingBean {@Resourceprivate GoodsService goodsService;@Resourceprivate SeckillOrderService seckillOrderService;@Resourceprivate OrderService orderService;@Resourceprivate RedisTemplate redisTemplate;@RequestMapping(value = "/doSeckill")public String doSeckill(Model model, User user, Long goodsId) {System.out.println("-----秒杀 V3.0--------");//===================秒杀 v3.0 start =========================if (user == null) {return "login";}model.addAttribute("user", user);GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);//判断库存if (goodsVo.getStockCount() < 1) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}//解决重复抢购,直接到redis中获取对应的秒杀订单,如果有则说明已经抢购了,不能继续抢购SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);if (seckillOrder != null) {model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}//库存预减,如果在redis中预减库存,发现秒杀商品中已经没有库存了,直接返回//从而减少这个方法请求 orderService.seckill防止大量请求打到数据,优化秒杀//这个方法具有原子性!!!Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);//说明这个商品已经没有库存了if (decrement < 0) {//恢复redis库存为0,对业务没有影响只是为了好看redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}//抢购Order order = orderService.seckill(user, goodsVo);if (order == null) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}model.addAttribute("order", order);model.addAttribute("goods", goodsVo);return "orderDetail";//===================秒杀 v3.0 end... =========================}//该方法实在类的所有属性都初始化后,自动执行//这里将所有秒杀商品的库存量加载到redis中@Overridepublic void afterPropertiesSet() throws Exception {//1.查询所有的秒杀商品List<GoodsVo> list = goodsService.findGoodsVo();if (CollectionUtils.isEmpty(list)) {return;}//遍历list,将秒杀商品的库存量放入到redis中//秒杀商品库存量对应的key seckillGoods:商品idfor (GoodsVo goodsVo : list) {redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());}}}
大体思路:
-
项目启动时加载秒杀商品库存到redis中,库存量随着每次启动都会更新
-
在进行抢购之前先到redis中进行库存预减,利用
redis的decrement具备原子性的特点 -
如果redis预减库存小于0则直接返回,避免了多余的请求打到数据库
3.4 v4.0 优化秒杀:加入内存标记,避免总到 Reids 查询库存
- 需求分析/图解
- 如果某个商品库存已经为空了, 我们仍然是到 Redis 去查询的, 还可以进行优化
- 解决方案: 给商品进行内存标记, 如果库存为空, 直接返回, 避免总是到 Redis 查询库存
- 分析思路-示意图

@Controller
@RequestMapping(value = "/seckill")
public class SeckillController implements InitializingBean {@Resourceprivate GoodsService goodsService;@Resourceprivate SeckillOrderService seckillOrderService;@Resourceprivate OrderService orderService;@Resourceprivate RedisTemplate redisTemplate;@RequestMapping(value = "/doSeckill")public String doSeckill(Model model, User user, Long goodsId) {System.out.println("-----秒杀 V4.0--------");//===================秒杀 v4.0 start =========================if (user == null) {return "login";}model.addAttribute("user", user);GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);//判断库存if (goodsVo.getStockCount() < 1) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}//解决重复抢购,直接到redis中获取对应的秒杀订单,如果有则说明已经抢购了,不能继续抢购SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);if (seckillOrder != null) {model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}//如果库存为空,避免总是到 reids 去查询库存,给 redis 增加负担(内存标记)if (entryStockMap.get(goodsId)) {//如果当前这个秒杀商品已经是空库存,则直接返回.model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}//库存预减,如果在redis中预减库存,发现秒杀商品中已经没有库存了,直接返回//从而减少这个方法请求 orderService.seckill防止大量请求打到数据,优化秒杀//这个方法具有原子性!!!Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);//说明这个商品已经没有库存了if (decrement < 0) {//这里使用内存标记,避免多次操作 redis, true 表示空库存了.entryStockMap.put(goodsId, true);//恢复redis库存为0,对业务没有影响只是为了好看redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}//抢购Order order = orderService.seckill(user, goodsVo);if (order == null) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}model.addAttribute("order", order);model.addAttribute("goods", goodsVo);return "orderDetail";//===================秒杀 v4.0 end... =========================}//该方法实在类的所有属性都初始化后,自动执行//这里将所有秒杀商品的库存量加载到redis中@Overridepublic void afterPropertiesSet() throws Exception {//1.查询所有的秒杀商品List<GoodsVo> list = goodsService.findGoodsVo();if (CollectionUtils.isEmpty(list)) {return;}//遍历list,将秒杀商品的库存量放入到redis中//秒杀商品库存量对应的key seckillGoods:商品idfor (GoodsVo goodsVo : list) {redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());//当有库存为 false,当无库存为 true。防止库存没有了,还会到 Redis 进行判断操作entryStockMap.put(goodsVo.getId(), false);}}}
3.4 v5.0 优化秒杀: 加入消息队列,实现秒杀的异步请求
需求分析/图解
- 问题分析
前面秒杀, 没有实现异步机制, 是完成下订单后, 再返回的, 当有大并发请求下订单操作时, 数据库来不及响应, 容易造成线程堆积
- 解决方案
-
加入消息队列,实现秒杀的异步请求
-
接收到客户端秒杀请求后,服务器立即返回 正在秒杀中…, 有利于流量削峰
-
客户端进行轮询秒杀结果, 接收到秒杀结果后,在客户端页面显示即可
-
秒杀消息发送设计 SeckillMessage - String


代码实现
秒杀消息类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SeckillMessage {private User user;private Long goodsId;
}
定义消息队列、交换机
@Configuration
public class RabbitMQSecKillConfig {private static final String QUEUE = "seckillQueue";private static final String EXCHANGE = "seckillExchange";@Beanpublic Queue queue_seckill() {return new Queue(QUEUE);}@Beanpublic TopicExchange topicExchange_seckill() {return new TopicExchange(EXCHANGE);}@Beanpublic Binding binding_seckill() {return BindingBuilder.bind(queue_seckill()).to(topicExchange_seckill()).with("seckill.#");}}
发送秒杀消息
@Service
@Slf4j
public class MQSenderMessage {@Resourceprivate RabbitTemplate rabbitTemplate;//发送秒杀信息public void senderMessage(String message) {System.out.println("发送消息了=" + message);log.info("发送消息:" + message);rabbitTemplate.convertAndSend("seckillExchange", "seckill.message", message);}
}
消息接收者
@Service
@Slf4j
public class MQReceiverConsumer {@Resourceprivate GoodsService goodsService;@Resourceprivate OrderService orderService;//下单操作@RabbitListener(queues = "seckillQueue")public void queue(String message) {SeckillMessage seckillMessage = JSONUtil.toBean(message, SeckillMessage.class);Long goodId = seckillMessage.getGoodsId();User user = seckillMessage.getUser();//获取抢购的商品信息GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodId);//下单操作orderService.seckill(user, goodsVo);}}
SeckillController
@Controller
@RequestMapping(value = "/seckill")
public class SeckillController implements InitializingBean {@Resourceprivate GoodsService goodsService;@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate MQSenderMessage mqSenderMessage;//如果某个商品库存已经为空, 则标记到 entryStockMapprivate HashMap<Long, Boolean> entryStockMap = new HashMap<>();@RequestMapping(value = "/doSeckill")public String doSeckill(Model model, User user, Long goodsId) {System.out.println("-----秒杀 V5.0--------");//===================秒杀 v5.0 start =========================if (user == null) {return "login";}model.addAttribute("user", user);GoodsVo goodsVo = goodsService.findGoodsVoByGoodsId(goodsId);//判断库存if (goodsVo.getStockCount() < 1) {model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}//解决重复抢购,直接到redis中获取对应的秒杀订单,如果有则说明已经抢购了,不能继续抢购SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsId);if (seckillOrder != null) {model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}//如果库存为空,避免总是到 reids 去查询库存,给 redis 增加负担(内存标记)if (entryStockMap.get(goodsId)) {//如果当前这个秒杀商品已经是空库存,则直接返回.model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());return "secKillFail";}//库存预减,如果在redis中预减库存,发现秒杀商品中已经没有库存了,直接返回//从而减少这个方法请求 orderService.seckill防止大量请求打到数据,优化秒杀//这个方法具有原子性!!!Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);//说明这个商品已经没有库存了if (decrement < 0) {//这里使用内存标记,避免多次操作 redis, true 表示空库存了.entryStockMap.put(goodsId, true);//恢复redis库存为0,对业务没有影响只是为了好看redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);model.addAttribute("errmsg", RespBeanEnum.REPEATE_ERROR.getMessage());return "secKillFail";}//抢购// Order order = orderService.seckill(user, goodsVo);// if (order == null) {// model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());// return "secKillFail";// }// model.addAttribute("order", order);// model.addAttribute("goods", goodsVo);//抢购, 向 MQ 发消息, 因为不知道是否成功, 客户端需要轮询//errmsg 为排队中.... , 暂时返回 "secKillFaill" 页面SeckillMessage seckillMessage =new SeckillMessage(user, goodsId);mqSenderMessage.senderMessage(JSONUtil.toJsonStr(seckillMessage));model.addAttribute("errmsg", "排队中.....");return "orderDetail";//===================秒杀 v5.0 end... =========================}//该方法实在类的所有属性都初始化后,自动执行//这里将所有秒杀商品的库存量加载到redis中@Overridepublic void afterPropertiesSet() throws Exception {//1.查询所有的秒杀商品List<GoodsVo> list = goodsService.findGoodsVo();if (CollectionUtils.isEmpty(list)) {return;}//遍历list,将秒杀商品的库存量放入到redis中//秒杀商品库存量对应的key seckillGoods:商品idfor (GoodsVo goodsVo : list) {redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());//当有库存为 false,当无库存为 true。防止库存没有了,还会到 Redis 进行判断操作entryStockMap.put(goodsVo.getId(), false);}}}
- 客户端轮询秒杀结果-思路分析示意图


相关文章:
秒杀高并发解决方案
秒杀高并发解决方案 1.秒杀/高并发方案-介绍 秒杀/高并发 其实主要解决两个问题,一个是并发读,一个是并发写并发读的核心优化理念是尽量减少用户到 DB 来"读"数据,或者让他们读更少的数据, 并 发写的处理原则也一样针对秒杀系统需…...
【每日一题】蓝桥杯加练 | Day07
文章目录一、三角回文数1、问题描述2、解题思路3、AC代码一、三角回文数 原题链接:三角回文数 1、问题描述 对于正整数 n, 如果存在正整数 k 使得n123⋯k k(k1)2\frac{k(k1)}{2}2k(k1) , 则 n 称为三角数。例如, 66066 是一个三角数, 因为 66066123⋯363 。 如果一…...
条件语句(分支语句)——“Python”
各位CSDN的uu们你们好呀,最近总是感觉特别特别忙,但是却又不知道到底干了些什么,好像啥也没有做,还忙得莫名其妙,言归正传,今天,小雅兰的内容还是Python呀,介绍一些顺序结构的知识点…...
论文投稿指南——中文核心期刊推荐(国家财政)
【前言】 🚀 想发论文怎么办?手把手教你论文如何投稿!那么,首先要搞懂投稿目标——论文期刊 🎄 在期刊论文的分布中,存在一种普遍现象:即对于某一特定的学科或专业来说,少数期刊所含…...
面向数据安全共享的联邦学习研究综述
开放隐私计算 摘 要:跨部门、跨地域、跨系统间的数据共享是充分发挥分布式数据价值的有效途径,但是现阶段日益严峻的数据安全威胁和严格的法律法规对数据共享造成了诸多挑战。联邦学习可以联合多个用户在不传输本地数据的情况下协同训练机器学习模型&am…...
Redis经典五种数据类型底层实现原理解析
目录总纲redis的k,v键值对新的三大类型五种经典数据类型redisObject结构图示结构讲解数据类型与数据结构关系图示string数据类型三大编码格式SDS详解代码结构为什么要重新设计源码解析三大编码格式hash数据类型ziplist和hashtable编码格式ziplist详解结构剖析ziplist的优势(为什…...
Jackson 返回前端的 Response结果字段大小问题
目录 1、问题产生的背景 2、出现的现象 3、解决方案 4、成果展现 5、总结 6、参考文章 1、问题产生的背景 因为本人最近工作相关的对接外部项目,在我们国内有很多程序员都是使用汉语拼音或者部分字母加上英文复合体定义返回实体VO,这样为了能够符合…...
每天五分钟机器学习:你理解贝叶斯公式吗?
本文重点 贝叶斯算法是机器学习算法中非常经典的算法,也是非常古老的一个算法,但是它至今仍然发挥着重大的作用,本节课程及其以后的专栏将会对贝叶斯算法来做一个简单的介绍。 贝叶斯公式 贝叶斯公式是由联合概率推导而来 其中p(Y|X)称为后验概率,P(Y)称为先验概率…...
C++的入门
C的关键字 C总计63个关键字,C语言32个关键字 命名空间 我们C的就是建立在C语言之上,但是是高于C语言的,将C语言的不足都弥补上了,而命名空间就是为了弥补C语言的不足。 看一下这个例子。在C语言中会报错 #include<stdio.h>…...
数据的存储
类型的意义:使用这个类型开辟内存空间的大小(大小决定了使用范围)如何看待内存空间视角类型的基本归类整型家族浮点数家族构造类型指针类型空类型整型存储解构:整型在计算机中占用四个字节,整型分为无符号整型和有符号整型在计算机…...
Linux查看UTC时间
先了解一下几个时间概念。 GMT时间:Greenwich Mean Time,格林尼治平时,又称格林尼治平均时间或格林尼治标准时间。是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间。 GMT时间存在较大误差,因此不再被作为标准时间使用。现在…...
SpringBoot修改启动图标(详细步骤)
目录 一、介绍 二、操作步骤 三、介绍Java学习(题外话) 四、关于基础知识 一、介绍 修改图标就是在资源加载目录(resources)下放一个banner.txt文件。这样运行加载的时候就会扫描到这个文件,然后启动的时候就会显…...
【每日一题Day143】面试题 17.05. 字母与数字 | 前缀和+哈希表
面试题 17.05. 字母与数字 给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。 返回该子数组,若存在多个最长子数组,返回左端点下标值最小的子数组。若不存在这样的数组,返回一个空数组。…...
Go 内置运算符 if for switch
算数运算符fmt.Println("103", 103) //103 13 fmt.Println("10-3", 10-3) //10-3 7 fmt.Println("10*3", 10*3) //10*3 30 //除法注意:如果运算的数都是整数,那么除后,去掉小数部分,保留整数部分 f…...
C语言指针数组实际应用(嵌入式)
C语言指针数组详细学习 指针是C语言中非常重要的概念之一,它可以让我们直接访问内存中的数据。指针数组则是由多个指针组成的数组,每个指针都可以指向内存中的某个位置。以下是一些指针数组的实际代码应用: 字符串数组 char* names[] {&q…...
常用的Java注解详解
Java是一种常用的编程语言,而注解是Java语言中非常重要的一部分。在这篇文章中,我们将介绍一些常用的Java注解,以及它们的作用和使用方法。 Override override注解是用于表示一个方法是被覆盖的。在Java中,如果子类要覆盖父类的方…...
华为OD机试题 - 第 K 个最小码值的字母(JavaScript)| 机考必刷
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:第 K 个最小码值的字母题目输入输出示例一输入输出说明示例一输…...
vscode环境配置(支持跳转,阅读linux kernel)
目录 1.卸载clangd插件 2.安装C插件 3. 搜索框内输入 “intell”,将 C_Cpp:Intelli Sense Engine 开关设置为 Default。 4.ubuntu安装global工具 5.vscode安装插件 6.验证是否生效 7.建立索引 1.卸载clangd插件 在插件管理中卸载clangd插件 2.安…...
zigbee学习笔记:IO操作
1、IAR新建工程 (1)Projetc→Create New Projetc→OK→选择位置,确定 (2)新建一个c文件,保存在路径中 (3)点击工程,右键→add→加入c文件 (4)…...
华为OD机试题 - 最少数量线段覆盖(JavaScript)| 机考必刷
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 华为OD机试题 最近更新的博客使用说明本篇题解:最少数量线段覆盖题目输入输出示例一输入输出说明Code解题思路版…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
