MQ消息太快导致的异步数据查询问题

本文针对MQ消息发送过快导致接收端查询到旧数据的问题,提出改进方案:将消息发送操作放置于事务提交之后,并通过检查当前线程是否处于事务中决定消息发送时机。
先说问题:系统用到mq发送消息,实际运行时发现mq的接收端程序在接收到消息之后,去查询业务数据的状态时发现仍然是老的状态,导致程序处理数据异常;手动去数据查询时发现是新的状态。
经分析,应该是mqserver和应用在同一个机器,mq消息太快,导致接收端程序查询时,发送端程序的数据库事务还没有提交完成,所以查询到的结果是老的数据。
如果你想思考一下,那就先暂停
-----------------------以下是解决方案------------------------------------------------------
解决方案:最初的方案是接收端延迟执行,拿到消息后 sleep1s,然后再执行,但是这是个很不好的处理,因为1s这个时间对发送端程序来说是不确定的,有可能1s是不够用的,那就用2s,3s?真不是一个好方案,但是勉强能用。
方案改进:后来又再次遇到了同样的问题,于是思考了一番,如下改进步骤:
改进1:既然与事务有关,那能不能把消息发送放在事务提交后?
实现:程序调用mq代理接口发送消息时并不真正的发送消息,而是把消息存储在ThreadLocal中,系统采用的是 springboot的事务管理:DataSourceTransactionManager,实现自定义MyDataSourceTransactionManager 继承 DataSourceTransactionManager,重写 doCommit 方法,在调用完父类的doCommit之后,查看 ThreadLocal中是否有消息,有的话就调用真正的mq发送接口发送消息;重写doCleanupAfterCompletion 方法,调用父类的doCleanupAfterCompletion 后,清理ThreadLocal中的消息。
问题:有的消息发送可能和事务无关,或者发送消息的线程中没有事务,这样就会导致消息没有真正发送
改进2:既然有没有事务的,那是不是可以在程序调用mq代理接口时查询当前线程是否有正在进行的事务,如果有,就存储消息到ThreadLocal,如果没有,就直接发送
实现:可通过 DataSourceTransactionManager.isExistingTransaction来实现
 
问题:在有事务的情况下,事务提交时发送消息,会不会影响到事务的提交?mq发送消息是网络调用,会不会影响程序的效率?
改进3,自己想把
<think>好的,用户之前问了Redis支持的数据类型和使用场景,现在又问消息队列的使用场景。可能需要先确定用户的使用场景,是技术选型还是学习了解? 首先,用户可能在学习消息队列的相关知识,或者在实际项目中需要引入消息队列解决问题。之前关于Redis的回答提到了List和Stream作为消息队列的用途,可能用户对此感兴趣,想进一步了解消息队列的应用。 消息队列的使用场景有很多,需要分类整理。比如系统解耦、异步处理、流量削峰、数据同步等。可能用户对这些概念不太清楚,需要具体例子来说明。 另外,用户可能关心不同场景下如何选择消息队列,比如Kafka、RabbitMQ、RocketMQ等的适用情况。但问题只问到使用场景,暂时不需要深入产品比较。 需要确保回答结构清晰,每个场景有实际例子,帮助用户理解。例如,订单系统和库存系统的解耦,用户注册后的异步处理,秒杀活动的流量控制等。 还要注意用户可能的误区,比如消息队列的延迟问题消息丢失处理,顺序性保证等,但问题只问使用场景,暂时不涉及这些细节。 是否需要提到消息队列的优势,如可靠性、扩展性等?可能作为场景的补充说明。 另外,用户可能想知道什么时候应该使用消息队列,避免过度设计。可以结合场景说明适用情况。 最后,总结消息队列的核心价值,帮助用户形成整体认识。</think>消息队列(Message Queue)是分布式系统中实现**异步通信**的核心组件,其核心价值在于**解耦系统、缓冲流量、保证可靠性**。以下是典型使用场景及示例: --- ### 一、核心应用场景 1. **系统解耦** - **场景痛点**:多个系统直接调用导致耦合,例如订单系统需同步调用库存、物流、支付系统 - **解决方案**:订单系统将事件写入消息队列,下游系统各自订阅 - **优势**:新增系统只需订阅消息,无需修改订单系统代码 - **示例**: ```bash # 订单系统发布事件 PUBLISH order_created "order_id=1001, product_id=42" # 库存系统订阅 SUBSCRIBE order_created → 扣减库存 # 物流系统订阅 SUBSCRIBE order_created → 生成运单 ``` 2. **异步处理** - **场景痛点**:同步操作链路过长导致响应延迟(如用户注册后需发邮件、初始化资料、送积分) - **解决方案**:非核心流程异步化,主流程速返回 - **效果**:注册接口响应时间从2秒降至200ms - **示例**: ```python def user_register(): save_user_to_db() # 同步操作 mq.publish("user_registered", user_id) # 异步触发后续操作 return "注册成功" # 立即响应 # 消息消费者异步处理 def consume_message(): send_welcome_email() init_user_profile() grant_signup_bonus() ``` 3. **流量削峰** - **场景痛点**:秒杀活动瞬时流量冲击导致数据库崩溃 - **解决方案**:用消息队列做缓冲层,平滑处理请求 - **实施步骤**: 1. 请求先进入队列(如RabbitMQ/Kafka) 2. 后端以可控速度消费(如每秒处理1000订单) 3. 前端显示"排队中"状态 - **数据对比**: | 方案 | 吞吐量 | 系统稳定性 | 用户体验 | |------------|-----------|------------|----------------| | 直接写库 | 500 TPS | 宕机风险高 | 直接报错 | | 消息队列 | 10万 TPS | 平稳运行 | 排队进度可视化 | 4. **数据同步** - **场景痛点**:MySQL到Elasticsearch的双写一致性难以保证 - **解决方案**:通过Binlog监听+消息队列实现最终一致性 - **架构流程**: ```mermaid graph LR MySQL -->|Canal监听Binlog| MQ -->|消费者| Elasticsearch ``` --- ### 二、进阶使用模式 5. **分布式事务** - **场景**:跨服务事务(如银行转账) - **方案**:事务消息(如RocketMQ Half Message) - **流程**: 1. 发送预备消息 2. 执行本地事务 3. 根据结果提交/回滚消息 6. **顺序消费** - **场景**:订单状态变更(创建→付款→发货)必须有序 - **实现**: - Kafka通过Partition保证分区内有序 - RocketMQ的MessageQueue顺序写 7. **延迟队列** - **场景**: - 订单30分钟未支付自动关闭 - 定时提醒(会议开始前15分钟通知) - **实现方式**: - RabbitMQ死信队列+TTL - RocketMQ延迟消息(18个预设级别) --- ### 三、技术选型建议 | 场景特征 | 推荐消息队列 | 关键优势 | |------------------------|--------------------|----------------------------| | 高吞吐日志收集 | Apache Kafka | 百万级TPS,持久化存储 | | 复杂路由需求 | RabbitMQ | 灵活Exchange绑定规则 | | 金融级事务消息 | RocketMQ | 完整事务消息机制 | | 云原生环境 | AWS SQS/NATS | 全托管服务,自动扩展 | --- ### 四、使用注意事项 1. **消息丢失防护** - 生产者:启用confirm机制(RabbitMQ)或ACK(Kafka) - 消费者:手动确认+幂等处理 2. **队列监控** - 关键指标:积压消息数、消费延迟、错误率 - 工具:Prometheus+Grafana监控看板 3. **容量规划** - 计算公式: ``` 所需存储 = 消息平均大小 × 峰值TPS × 保留时间 ``` - 示例:1KB/消息 × 5000 TPS × 保留7天 ≈ 2.9TB 消息队列如同系统的"关节",在提升扩展性的同时,也需要关注消息治理。根据业务特性选择合适的队列产品和架构模式,才能最大化发挥其价值。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值