分布式并发场景的核心问题与解决方案
文章目录
- 分布式并发场景的核心问题与解决方案
- 一、核心问题分析
- 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…...
 
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
 
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
 
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
 
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
 
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
 
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
 
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
 
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
