分布式并发场景的核心问题与解决方案
文章目录
- 分布式并发场景的核心问题与解决方案
- 一、核心问题分析
- 1. 分布式事务问题
- 2. 数据一致性问题
- 3. 并发控制问题
- 4. 分布式锁失效问题
- 二、解决方案
- 1. 分布式事务解决方案
- 1.1 可靠消息最终一致性方案
- 1.2 TCC方案实现
- 2. 缓存一致性解决方案
- 2.1 延迟双删策略
- 2.2 Canal方案
- 3. 并发控制解决方案
- 3.1 基于Redis的原子操作
- 3.2 防重复提交
- 三、系统监控与告警
- 1. 分布式链路追踪
- 2. 监控指标收集
- 四、最佳实践建议
- 五、注意事项
分布式并发场景的核心问题与解决方案
一、核心问题分析
1. 分布式事务问题
在分布式环境下,一个业务操作可能横跨多个服务,比如创建订单时涉及:
- 订单服务:创建订单
- 库存服务:扣减库存
- 支付服务:冻结余额
- 积分服务:赠送积分
可能出现的问题:
- 部分服务成功,部分服务失败
- 网络超时导致事务状态不确定
- 服务宕机导致事务中断
2. 数据一致性问题
在分布式系统中,由于CAP理论的限制,我们通常需要在一致性和可用性之间做出选择。
典型场景:
- 主从数据库的数据同步延迟
- 分布式缓存的数据一致性
- 跨服务的数据依赖
3. 并发控制问题
多个节点同时处理请求时的并发控制:
- 超卖问题
- 重复下单
- 数据竞争
4. 分布式锁失效问题
- Redis主从切换导致锁失效
- 时钟不同步导致的锁判断错误
- 网络分区导致的锁状态不一致
二、解决方案
1. 分布式事务解决方案
1.1 可靠消息最终一致性方案
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@Autowiredprivate RocketMQTemplate rocketMQTemplate;@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate MessageMapper messageMapper;@Transactional(rollbackFor = Exception.class)public void createOrder(OrderDTO orderDTO) {// 1. 事务消息表记录TransactionMessage message = new TransactionMessage();message.setMessageId(UUID.randomUUID().toString());message.setMessage(JSON.toJSONString(orderDTO));message.setStatus(MessageStatus.PREPARING);messageMapper.insert(message);// 2. 创建订单Order order = convertToOrder(orderDTO);orderMapper.insert(order);// 3. 发送事务消息sendTransactionMessage(message);}private void sendTransactionMessage(TransactionMessage message) {Message msg = MessageBuilder.withPayload(message).setHeader("messageId", message.getMessageId()).build();TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction("ORDER_TOPIC",msg,null);if (result.getLocalTransactionState() != LocalTransactionState.COMMIT_MESSAGE) {throw new BusinessException("发送事务消息失败");}}
}
消息消费者实现:
@Component
@RocketMQMessageListener(topic = "ORDER_TOPIC",consumerGroup = "order-consumer-group"
)
public class OrderMessageListener implements RocketMQListener<Message> {@Autowiredprivate StockService stockService;@Autowiredprivate MessageMapper messageMapper;@Overridepublic void onMessage(Message message) {String messageId = message.getHeaders().get("messageId", String.class);// 1. 检查消息是否已处理if (messageMapper.checkProcessed(messageId)) {return;}try {// 2. 处理业务逻辑TransactionMessage txMessage = JSON.parseObject(new String((byte[]) message.getPayload()),TransactionMessage.class);OrderDTO orderDTO = JSON.parseObject(txMessage.getMessage(),OrderDTO.class);// 3. 扣减库存stockService.decreaseStock(orderDTO.getProductId(), orderDTO.getQuantity());// 4. 更新消息状态messageMapper.markAsProcessed(messageId);} catch (Exception e) {// 5. 失败处理messageMapper.markAsFailed(messageId, e.getMessage());// 根据业务需求决定是否抛出异常重试throw e;}}
}
1.2 TCC方案实现
@Service
public class OrderTccServiceImpl implements OrderTccService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate StockTccService stockTccService;@Autowiredprivate PaymentTccService paymentTccService;@GlobalTransactionalpublic void createOrder(OrderDTO orderDTO) {// 1. Try阶段// 1.1 订单服务TryOrder order = prepareTryOrder(orderDTO);// 1.2 库存服务TrystockTccService.tryDecrease(orderDTO.getProductId(),orderDTO.getQuantity());// 1.3 支付服务TrypaymentTccService.tryFreeze(orderDTO.getUserId(),orderDTO.getAmount());}// Try阶段的订单处理private Order prepareTryOrder(OrderDTO orderDTO) {Order order = convertToOrder(orderDTO);order.setStatus(OrderStatus.TRY);orderMapper.insert(order);return order;}// Confirm阶段的订单处理public void confirmOrder(BusinessActionContext context) {String orderId = context.getActionContext("orderId").toString();Order order = orderMapper.selectById(orderId);order.setStatus(OrderStatus.CONFIRMED);orderMapper.updateById(order);}// Cancel阶段的订单处理public void cancelOrder(BusinessActionContext context) {String orderId = context.getActionContext("orderId").toString();Order order = orderMapper.selectById(orderId);order.setStatus(OrderStatus.CANCELED);orderMapper.updateById(order);}
}
2. 缓存一致性解决方案
2.1 延迟双删策略
@Service
public class ProductServiceImpl implements ProductService {@Autowiredprivate ProductMapper productMapper;@Autowiredprivate RedisTemplate<String, Product> redisTemplate;@Autowiredprivate ThreadPoolExecutor threadPoolExecutor;private static final String PRODUCT_CACHE_KEY = "product:";private static final long DELAY_DELETE_TIME = 1000; // 1秒@Transactional(rollbackFor = Exception.class)public void updateProduct(Product product) {// 1. 删除缓存String cacheKey = PRODUCT_CACHE_KEY + product.getId();redisTemplate.delete(cacheKey);// 2. 更新数据库productMapper.updateById(product);// 3. 延迟双删threadPoolExecutor.execute(() -> {try {Thread.sleep(DELAY_DELETE_TIME);redisTemplate.delete(cacheKey);} catch (InterruptedException e) {Thread.currentThread().interrupt();log.error("延迟双删失败", e);}});}
}
2.2 Canal方案
@Component
public class ProductCanalClient {@Autowiredprivate RedisTemplate<String, Product> redisTemplate;@Listen(table = "product")public void handleProductChange(CanalEntry.Entry entry) {if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) {CanalEntry.RowChange rowChange = entry.getRowChange();for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {if (rowChange.getEventType() == CanalEntry.EventType.UPDATE) {// 处理更新事件handleProductUpdate(rowData);} else if (rowChange.getEventType() == CanalEntry.EventType.DELETE) {// 处理删除事件handleProductDelete(rowData);}}}}private void handleProductUpdate(CanalEntry.RowData rowData) {// 解析变更数据Map<String, String> data = parseRowData(rowData.getAfterColumnsList());String productId = data.get("id");// 更新缓存String cacheKey = "product:" + productId;Product product = convertToProduct(data);redisTemplate.opsForValue().set(cacheKey, product);}
}
3. 并发控制解决方案
3.1 基于Redis的原子操作
@Service
public class StockService {@Autowiredprivate StringRedisTemplate redisTemplate;private static final String STOCK_KEY = "product:stock:";public boolean decreaseStock(Long productId, Integer quantity) {String key = STOCK_KEY + productId;// Lua脚本保证原子性String script = "local stock = redis.call('get', KEYS[1]) " +"if stock and tonumber(stock) >= tonumber(ARGV[1]) then " +" return redis.call('decrby', KEYS[1], ARGV[1]) " +"end " +"return -1";DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptText(script);redisScript.setResultType(Long.class);Long result = redisTemplate.execute(redisScript,Collections.singletonList(key),quantity.toString());return result != null && result >= 0;}
}
3.2 防重复提交
@Aspect
@Component
public class RepeatSubmitAspect {@Autowiredprivate StringRedisTemplate redisTemplate;@Around("@annotation(repeatSubmit)")public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String token = request.getHeader("token");String key = getRepeatSubmitKey(joinPoint, token);// 使用Redis的setIfAbsent实现防重boolean isNotRepeat = redisTemplate.opsForValue().setIfAbsent(key,"1",repeatSubmit.interval(),TimeUnit.MILLISECONDS);if (!isNotRepeat) {throw new BusinessException("请勿重复提交");}return joinPoint.proceed();}
}
三、系统监控与告警
1. 分布式链路追踪
@Configuration
public class SleuthConfig {@Beanpublic Sampler defaultSampler() {return Sampler.ALWAYS_SAMPLE;}
}
2. 监控指标收集
@Component
public class DistributedMetrics {@Autowiredprivate MeterRegistry registry;// 记录分布式锁获取情况private Counter lockCounter;private Timer lockTimer;@PostConstructpublic void init() {lockCounter = registry.counter("distributed.lock.acquire");lockTimer = registry.timer("distributed.lock.time");}public void recordLockAcquire(String lockKey, boolean success) {lockCounter.increment();Tags tags = Tags.of("lock_key", lockKey,"success", String.valueOf(success));registry.counter("distributed.lock.acquire", tags).increment();}public void recordLockTime(String lockKey, long timeMillis) {lockTimer.record(timeMillis, TimeUnit.MILLISECONDS);}
}
四、最佳实践建议
-
业务设计层面:
- 尽量避免复杂分布式事务
- 考虑业务可补偿性
- 合理设计重试机制
-
技术选型层面:
- 优先考虑消息队列解耦
- 合理使用缓存
- 选择合适的分布式事务方案
-
监控运维层面:
- 完善的监控系统
- 合理的告警阈值
- 灾难恢复预案
-
性能优化层面:
- 合理的数据分片策略
- 避免长事务
- 批量处理优化
五、注意事项
-
数据库层面:
- 避免大事务
- 合理设计索引
- 注意死锁问题
-
缓存层面:
- 防止缓存雪崩
- 注意缓存穿透
- 合理设置过期时间
-
消息队列层面:
- 保证消息可靠性
- 处理重复消息
- 注意消息顺序性
-
分布式锁层面:
- 防止锁失效
- 避免死锁
- 合理设置超时时间
相关文章:
分布式并发场景的核心问题与解决方案
文章目录 分布式并发场景的核心问题与解决方案一、核心问题分析1. 分布式事务问题2. 数据一致性问题3. 并发控制问题4. 分布式锁失效问题 二、解决方案1. 分布式事务解决方案1.1 可靠消息最终一致性方案1.2 TCC方案实现 2. 缓存一致性解决方案2.1 延迟双删策略2.2 Canal方案 3.…...
D - Many Segments 2(ABC377)
题意:给定n和m,给定n个区间li,ri,求出满足区间lr不完全包含区间liri的个数 分析:用优先队列对区间r进行排序,i表示左区间,每次找到右区间加入即可。 代码: #include<bits/stdc…...
数组指针和指针数组的区别
数组指针和指针数组的区别 根据我个人的理解如下: 数组指针:指向数组的指针。着重点在于最后的指针两个字。 指针数组: 所有元素都是指针的数组。着重点在于最后的数组两个字。 另外来看助手的回答: Kimi: 1. **数组指针(Ar…...
【VUE点击父组件按钮,跳转到子组件】
要实现在Vue中,父组件通过点击按钮进入子组件的 <el-dialog> 弹窗,并在弹窗中嵌套 <el-table> 表格,可以按照以下步骤进行编写代码: 在父组件中,定义一个数据属性用于控制子组件弹窗的显示与隐藏。 data…...
Java列表排序:方法与实践
在Java编程中,列表排序是一个常见且重要的任务。本文将介绍Java中对列表进行排序的几种方法,包括使用Collections.sort()、List.sort()以及自定义排序规则。 1. 使用Collections.sort() Collections.sort()是Java提供的一个静态方法,用于对…...
哈希及其封装实现unordermap和set
哈希 直接定址法 哈希和之前的红黑树的区别就是,它是通过映射关系来找到目标的,可以把它想象成之前排序的计数排序,那其实就是哈希的一种方法,叫做直接定址法。 对于比较集中的数据,它只需要开一段区间,…...
在 AMD GPU 上构建解码器 Transformer 模型
Building a decoder transformer model on AMD GPU(s) — ROCm Blogs 2024年3月12日 作者 Phillip Dang. 在这篇博客中,我们展示了如何使用 PyTorch 2.0 和 ROCm 在单个节点上的单个和多个 AMD GPU 上运行Andrej Karpathy’s beautiful PyTorch re-implementation …...
Canvas简历编辑器-选中绘制与拖拽多选交互设计
Canvas简历编辑器-选中绘制与拖拽多选交互设计 在之前我们聊了聊如何基于Canvas与基本事件组合实现了轻量级DOM,并且在此基础上实现了如何进行管理事件以及多层级渲染的能力设计。那么此时我们就依然在轻量级DOM的基础上,关注于实现选中绘制与拖拽多选交…...
简单工厂(Simple Factory)
简单工厂(Simple Factory) 在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。 说明: 简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪…...
ffmpeg拉流分段存储到文件-笔记
通过ffmpeg可以从rtsp网络流拉取数据并存储到本地文件里,如下命令。做个笔记 ffmpeg -rtsp_transport tcp -i rtsp://192.168.1.168:6880/live -c copy -f segment -segment_time 60 stream_piece_%d.mp4这条 ffmpeg 命令的作用是从一个 RTSP 流中捕获视频ÿ…...
Java 实习工资大概是多少?——解读影响薪资的因素
文章目录 1. 城市因素:一线、二线的差距2. 公司类型:互联网公司、外企和传统企业的差别3. 个人能力:经验、技术栈的重要性4. 其他影响因素:学历和实习时间总结推荐阅读文章 Java 开发作为广泛应用的职业方向,实习工资的…...
【Linux】万字详解:Linux文件系统与软硬链接
🌈 个人主页:Zfox_ 🔥 系列专栏:Linux 目录 🚀 前言 一: 🔥 磁盘的物理结构二: 🔥 磁盘的存储结构 三: 🔥 磁盘的逻辑结构 四: &#…...
spacenavd
介绍spacenavd开源项目,主要是因为在斯坦福大学的UMI项目中使用了该项目。在斯坦福大学的 UMI(Universal Manipulation Interface)项目中,Spacenavd 主要用于处理 3D Space Mouse(空间鼠标)的输入…...
C#WPF的XAML的语法详谈和特性
WPF的XAML(eXtensible Application Markup Language)是一种基于XML的标记语言,用于在.NET框架中定义和描述用户界面。XAML提供了一种声明性的方式来构建应用程序的UI元素,包括窗口、控件、布局、样式、动画和数据绑定等。 XAML的…...
一篇文章讲透数据结构之二叉搜索树
前言 在前面的学习过程中,我们已经学习了二叉树的相关知识。在这里我们再使用C来实现一些比较难的数据结构。 这篇文章用来实现二叉搜索树。 一.二叉搜索树 1.1二叉搜索树的定义 二叉搜索树(Binary Search Tree)是基于二叉树的一种升级版…...
新手入门c++(8)
到时候了,是时候给你们讲一下其他的定义形式与格式化输入输出了。 1.长整型变量 长整型变量分为两种: ①long类型 在计算机编程中,long 类型是一个整型数据类型,用于存储较大的整数。它的大小和范围取决于操作系统和编译器的实…...
新手铲屎官提问,有哪几款噪音低的宠物空气净化器推荐
相信很多铲屎官都明白的的痛就是猫咪掉毛太严重,所以每次看到满天飞的浮毛时只想赶紧逃离,一点都不想清理。但是家是自己的,猫是自己的,健康也是自己的,不清理也得清理。 为了更有效的清理浮毛,我朋友特意…...
解决RabbitMQ脑裂问题
文章目录 前言一、现象二、解决办法 前言 RabbitMQ脑裂 一、现象 RabbitMQ镜像群出现脑裂现象,各个节点的MQ实例都“各自为政”,数据并不同步。 二、解决办法 # 停止mq sh rabbitmq-server stop_app # 查看mq进程是否存在 ps -ef | grep rabbitmq # …...
经纬恒润AUTOSAR成功适配芯钛科技Alioth TTA8车规级芯片
在汽车电子领域,功能安全扮演着守护者的角色,它确保了车辆在复杂多变的情况下保持稳定可靠的运行。随着汽车电子的复杂性增加,市场对产品功能安全的要求也日益提高。基于此背景,经纬恒润AUTOSAR基础软件产品INTEWORK-EAS-CP成功适…...
4、java random随机数、一维数组、二维数组
目录 Random类与随机数生成数组的概述与使用数组的内存分配与访问数组的常见问题与解决方案一维数组的遍历与操作二维数组的概述与遍历1. Random类与随机数生成 引言 在编程中,我们经常需要生成随机数,比如在游戏、模拟实验或者数据处理中。Java提供了一个非常方便的类Rand…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
HTML中各种标签的作用
一、HTML文件主要标签结构及说明 1. <!DOCTYPE html> 作用:声明文档类型,告知浏览器这是 HTML5 文档。 必须:是。 2. <html lang“zh”>. </html> 作用:包裹整个网页内容,lang"z…...
Docker 镜像上传到 AWS ECR:从构建到推送的全流程
一、在 EC2 实例中安装 Docker(适用于 Amazon Linux 2) 步骤 1:连接到 EC2 实例 ssh -i your-key.pem ec2-useryour-ec2-public-ip步骤 2:安装 Docker sudo yum update -y sudo amazon-linux-extras enable docker sudo yum in…...
