Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >聊一聊顺序消息

聊一聊顺序消息

作者头像
林一
发布于 2018-07-24 08:11:50
发布于 2018-07-24 08:11:50
1.4K00
代码可运行
举报
文章被收录于专栏:MessageQueueMessageQueue
运行总次数:0
代码可运行

当我们说顺序时,我们在说什么?

日常思维中,顺序大部分情况会和时间关联起来,即时间的先后表示事件的顺序关系。

比如事件A发生在下午3点一刻,而事件B发生在下午4点,那么我们认为事件A发生在事件B之前,他们的顺序关系为先A后B。

上面的例子之所以成立是因为他们有相同的参考系,即他们的时间是对应的同一个物理时钟的时间。如果A发生的时间是北京时间,而B依赖的时间是东京时间,那么先A后B的顺序关系还成立吗?

如果没有一个绝对的时间参考,那么A和B之间还有顺序吗,或者说怎么断定A和B的顺序?

显而易见的,如果A、B两个事件之间如果是有因果关系的,那么A一定发生在B之前(前因后果,有因才有果)。相反,在没有一个绝对的时间的参考的情况下,若A、B之间没有因果关系,那么A、B之间就没有顺序关系。

那么,我们在说顺序时,其实说的是:

  • 有绝对时间参考的情况下,事件的发生时间的关系;
  • 和没有时间参考下的,一种由因果关系推断出来的happening before的关系;

在分布式环境中讨论顺序

当把顺序放到分布式环境(多线程、多进程都可以认为是一个分布式的环境)中去讨论时:

  • 同一线程上的事件顺序是确定的,可以认为他们有相同的时间作为参考
  • 不同线程间的顺序只能通过因果关系去推断

(点表示事件,波浪线箭头表示事件间的消息)

上图中,进程P中的事件顺序为p1->p2->p3->p4(时间推断)。而因为p1给进程Q的q2发了消息,那么p1一定在q2之前(因果推断)。但是无法确定p1和q1之间的顺序关系。

推荐阅读《Time, Clocks, and the Ordering of Events in a Distributed System》,会透彻的分析分布式系统中的顺序问题。

消息中间件中的顺序消息

什么是顺序消息

有了上述的基础之后,我们回到本篇文章的主题中,聊一聊消息中间件中的顺序消息。

顺序消息(FIFO 消息)是 MQ 提供的一种严格按照顺序进行发布和消费的消息类型。顺序消息由两个部分组成:顺序发布和顺序消费。 顺序消息包含两种类型: 分区顺序:一个Partition内所有的消息按照先进先出的顺序进行发布和消费 全局顺序:一个Topic内所有的消息按照先进先出的顺序进行发布和消费

这是阿里云上对顺序消息的定义,把顺序消息拆分成了顺序发布和顺序消费。那么多线程中发送消息算不算顺序发布?

如上一部分介绍的,多线程中若没有因果关系则没有顺序。那么用户在多线程中去发消息就意味着用户不关心那些在不同线程中被发送的消息的顺序。即多线程发送的消息,不同线程间的消息不是顺序发布的,同一线程的消息是顺序发布的。这是需要用户自己去保障的。

而对于顺序消费,则需要保证哪些来自同一个发送线程的消息在消费时是按照相同的顺序被处理的(为什么不说他们应该在一个线程中被消费呢?)。

全局顺序其实是分区顺序的一个特例,即使Topic只有一个分区(以下不在讨论全局顺序,因为全局顺序将面临性能的问题,而且绝大多数场景都不需要全局顺序)。

如何保证顺序

在MQ的模型中,顺序需要由3个阶段去保障:

  1. 消息被发送时保持顺序
  2. 消息被存储时保持和发送的顺序一致
  3. 消息被消费时保持和存储的顺序一致

发送时保持顺序意味着对于有顺序要求的消息,用户应该在同一个线程中采用同步的方式发送。存储保持和发送的顺序一致则要求在同一线程中被发送出来的消息A和B,存储时在空间上A一定在B之前。而消费保持和存储一致则要求消息A、B到达Consumer之后必须按照先A后B的顺序被处理。

如下图所示:

对于两个订单的消息的原始数据:a1、b1、b2、a2、a3、b3(绝对时间下发生的顺序):

  • 在发送时,a订单的消息需要保持a1、a2、a3的顺序,b订单的消息也相同,但是a、b订单之间的消息没有顺序关系,这意味着a、b订单的消息可以在不同的线程中被发送出去
  • 在存储时,需要分别保证a、b订单的消息的顺序,但是a、b订单之间的消息的顺序可以不保证
    • a1、b1、b2、a2、a3、b3是可以接受的
    • a1、a2、b1、b2、a3、b3也是可以接受的
    • a1、a3、b1、b2、a2、b3是不能接受的
  • 消费时保证顺序的简单方式就是“什么都不做”,不对收到的消息的顺序进行调整,即只要一个分区的消息只由一个线程处理即可;当然,如果a、b在一个分区中,在收到消息后也可以将他们拆分到不同线程中处理,不过要权衡一下收益
开源RocketMQ中顺序的实现

上图是RocketMQ顺序消息原理的介绍,将不同订单的消息路由到不同的分区中。文档只是给出了Producer顺序的处理,Consumer消费时通过一个分区只能有一个线程消费的方式来保证消息顺序,具体实现如下。

Producer端

Producer端确保消息顺序唯一要做的事情就是将消息路由到特定的分区,在RocketMQ中,通过MessageQueueSelector来实现分区的选择。

  • List<MessageQueue> mqs:消息要发送的Topic下所有的分区
  • Message msg:消息对象
  • 额外的参数:用户可以传递自己的参数

比如如下实现就可以保证相同的订单的消息被路由到相同的分区:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
long orderId = ((Order) object).getOrderId;
return mqs.get(orderId % mqs.size());

Consumer端

RocketMQ消费端有两种类型:MQPullConsumer和MQPushConsumer。

MQPullConsumer由用户控制线程,主动从服务端获取消息,每次获取到的是一个MessageQueue中的消息。PullResult中的List msgFoundList自然和存储顺序一致,用户需要再拿到这批消息后自己保证消费的顺序。

对于PushConsumer,由用户注册MessageListener来消费消息,在客户端中需要保证调用MessageListener时消息的顺序性。RocketMQ中的实现如下:

  1. PullMessageService单线程的从Broker获取消息
  2. PullMessageService将消息添加到ProcessQueue中(ProcessMessage是一个消息的缓存),之后提交一个消费任务到ConsumeMessageOrderService
  3. ConsumeMessageOrderService多线程执行,每个线程在消费消息时需要拿到MessageQueue的锁
  4. 拿到锁之后从ProcessQueue中获取消息

保证消费顺序的核心思想是:

  • 获取到消息后添加到ProcessQueue中,单线程执行,所以ProcessQueue中的消息是顺序的
  • 提交的消费任务时提交的是“对某个MQ进行一次消费”,这次消费请求是从ProcessQueue中获取消息消费,所以也是顺序的(无论哪个线程获取到锁,都是按照ProcessQueue中消息的顺序进行消费)
顺序和异常的关系

顺序消息需要Producer和Consumer都保证顺序。Producer需要保证消息被路由到正确的分区,消息需要保证每个分区的数据只有一个线程消息,那么就会有一些缺陷:

  • 发送顺序消息无法利用集群的Failover特性,因为不能更换MessageQueue进行重试
  • 因为发送的路由策略导致的热点问题,可能某一些MessageQueue的数据量特别大
  • 消费的并行读依赖于分区数量
  • 消费失败时无法跳过

不能更换MessageQueue重试就需要MessageQueue有自己的副本,通过Raft、Paxos之类的算法保证有可用的副本,或者通过其他高可用的存储设备来存储MessageQueue。

热点问题好像没有什么好的解决办法,只能通过拆分MessageQueue和优化路由方法来尽量均衡的将消息分配到不同的MessageQueue。

消费并行度理论上不会有太大问题,因为MessageQueue的数量可以调整。

消费失败的无法跳过是不可避免的,因为跳过可能导致后续的数据处理都是错误的。不过可以提供一些策略,由用户根据错误类型来决定是否跳过,并且提供重试队列之类的功能,在跳过之后用户可以在“其他”地方重新消费到这条消息。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-05-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MessageQueue 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Mysql 异常:Lock wait timeout exceeded; try restarting transaction的解决办法
  接口响应时间超长,耗时几十秒才返回错误提示,后台日志中出现Lock wait timeout exceeded; try restarting transaction的错误
chenchenchen
2021/09/06
3.7K0
MySQL线上维护三脚本
1. 获取process和lock的现场信息(get_processlist.sh) #!/bin/bash source /home/mysql/.bashrc DT=`date '+%Y%m%d_%H%M%S'` mysql -uroot -p123456 -S /data/3306/mysqldata/mysql.sock -e "select t1.* from information_schema.processlist t1,(select count(1) c from informatio
用户1148526
2021/12/07
3550
MySQL 死锁的详细分析方法
用数据库的时候,偶尔会出现死锁,针对我们的业务系统,出现死锁的直接结果就是系统卡顿、客户找事儿,所以我们也在想尽全力的消除掉数据库的死锁。出现死锁的时候,如果只是想解锁,用show full processlist看下kill掉就好了,如果想查找到详细的问题,一个办法是用show engine innodb status来查看简略信息或者开死锁日志,后期在MySQL日志里面慢慢分析。以上这写方法我们都用过,最近在看Innodb的书的时候发现另一种实时的分析方法,能最大限度的分析死锁的原因。
星哥玩云
2022/08/17
5500
Mysql长事务总结
在结果中idletime是计算产生的,也是事务的持续时间。但事务的trxquery是NUL,这并不是说事务什么也没执行,一个事务可能包含多个SQL,如果SQL执行完毕就不再显示了。当前事务正在执行,innodb也不知道这个事务后续还有没有sql,啥时候会commit。 因此trx_query不能提供有意义的信息。
mingjie
2022/05/12
9150
Mysql长事务总结
pt-online-schema-change使用
如果说你的数据量并发量不大,或者你的数据量很少没有到千万级别,也许pt-osc、gh-osc,online-ddl这些工具都用不着。但是,如果你的数据量很大,数据又很热。如果你没有这些工具,你可能无法完成对一个数据库新增一个字段或者任何一个简单的DDL语句。
chengcheng222e
2021/11/03
9410
pt-online-schema-change使用
技术分享 | MySQL 行锁超时排查方法优化
之前在 [如何有效排查解决 MySQL 行锁等待超时问题] 文章中介绍了如何监控解决行锁超时报错,当时介绍的监控方案主要是以 shell 脚本 + general_log 来捕获行锁等待信息,后来感觉比较麻烦,因此优化后改成用 Event + Procedure 的方法定时在 MySQl 内执行,将行锁等待信息记录到日志表中,并且加入了 pfs 表中的事务上下文信息,这样可以省去登陆服务器执行脚本与分析 general_log 的过程,更加便捷。
爱可生开源社区
2021/02/26
5110
技术分享 | MySQL 行锁超时排查方法优化
MySQL-长事务详解
『入门MySQL』系列文章已经完结,今后我的文章还是会以MySQL为主,主要记录下近期工作及学习遇到的场景或者自己的感悟想法,可能后续的文章不是那么连贯,但还是希望大家多多支持。言归正传,本篇文章主要介绍MySQL长事务相关内容,比如说我们开启的一个事务,一直没提交或回滚会怎样呢,出现事务等待情况应该如何处理,本篇文章将给你答案。
MySQL技术
2019/09/23
3.4K0
MySQL探秘(五):InnoDB锁的类型和状态查询
 锁是数据库系统区分于文件系统的一个关键特性。数据库使用锁来支持对共享资源进行并发访问,提供数据的完整性和一致性。此外,数据库事务的隔离性也是通过锁实现的。InnoDB在此方面一直优于其他数据库引擎。InnoDB会在行级别上对表数据上锁,而MyISAM只能在表级别上锁,二者性能差异可想而知。
aoho求索
2018/12/12
1.1K0
MySQL探秘(五):InnoDB锁的类型和状态查询
MySQL找出未提交事务的信息
我们经常会碰到这样的情况,某个事务执行完了未提交,后续再来一个DDL和DML操作,导致后面的session要么处于waiting for metadata lock,要么是锁等待超时。这时我们往往只能找到这个未提交的事务的事务id和session id,但是一般都处于sleep状态,不好分析事务内容到底是什么,所以通常都是粗鲁地kill这个session后解决问题,但是应用层的研发人员往往找不到到底是哪个事务引起的,后面再出现问题时还要重复kill。
用户1148526
2020/03/31
5.2K0
InnoDB如何快速杀掉堵塞会话的思考
我们在运维MySQL的过程中,肯定多多少少遇到过Innodb row lock的问题,如果在线上遇到我们可能会看到一大片的session处于堵塞状态通常我们在show processlist中会看到如下:
老叶茶馆
2021/01/12
1.1K0
InnoDB如何快速杀掉堵塞会话的思考
MySQL 锁机制和事务
InnoDB存储引擎支持行级锁 其大类可以细分为共享锁和排它锁两类 共享锁(S):允许拥有共享锁的事务读取该行数据。当一个事务拥有一行的共享锁时,另外的事务可以在同一行数据也获得共享锁,但另外的事务无法获得同一行数据上的排他锁
星哥玩云
2022/08/17
8610
MySQL 锁机制和事务
Mysql一分钟定位 Next-Key Lock,你需要几分钟
查看线程模型 show variables like 'thread_handling'
王清培
2020/01/27
4170
mysql innodb_trx参数详解
1、innodb_trx表提供了当前innodb引擎内每个事务的信息(只读事务除外),包括当一个事务启动,事务是否在等待一个锁,以及交易正在执行的语句(如果有的话)。查询语句:
用户14527
2022/03/24
4.2K0
MySQL 死锁与日志二三事
最近线上 MySQL 接连发生了几起数据异常,都是在凌晨爆发,由于业务场景属于典型的数据仓库型应用,白天压力较小无法复现。甚至有些异常还比较诡异,最后 root cause 分析颇费周折。那实际业务当中咱们如何能快速的定位线上 MySQL 问题,修复异常呢?下文我会根据两个实际 case,分享下相关的经验与方法。 1、Case1:部分数据更新失败 某天渠道同学反馈某报表极个别渠道数据为 0,大部分渠道数据正常。这个数据是由一个统计程序每天凌晨例行更新的,按理来说,要么全部正常,要么全部失败,那会是什么原因导
用户1177713
2018/02/24
2.9K0
MySQL 死锁与日志二三事
DBBrain最佳实践:未提交事务的处理与应对
DBBrain 上经常会有用户来咨询“未提交事务”的事件会有什么问题,该如何处理等。其实这个问题的影响属于可大可小,所以正好来专门分析一下,避免因为轻视了这个问题导致严重的业务故障。
王文安@DBA
2022/03/03
2.9K2
DBBrain最佳实践:未提交事务的处理与应对
MySQL/MariaDB的锁超详细讲解
使用begin或者start transaction来显式开启一个事务,显式开启的事务必须使用commit或者rollback显式提交或回滚。几种特殊的情况除外:行版本隔离级别下的更新冲突和死锁会自动回滚。
星哥玩云
2022/08/16
1.2K0
MySQL/MariaDB的锁超详细讲解
【MySQL】说透锁机制(三)行锁升表锁如何避免? 锁表了如何排查?
在上文我们曾小小的提到过,在索引失效的情况下,MySQL会把所有聚集索引记录和间隙都锁上,我们称之为锁表,或叫行锁升表锁.
天罡gg
2022/12/29
3K0
【MySQL】说透锁机制(三)行锁升表锁如何避免? 锁表了如何排查?
MySQL锁等待问题
昨天有个项目 一直登陆不上去,查看日志报错信息:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
sunonzj
2022/06/21
7140
MySQL锁等待问题
[1156]MySQL数据库可用性监控脚本
从mysql性能字典表,innodb_lock_waits 是锁信息,innodb_trx是事务信息,有两条记录,需要放到一行中,因此,需要关联2次。
周小董
2022/08/23
1.2K0
为什么MySQL没有负载,但交易却跑不动?
在MySQL的数据库中,我们有时会发现MySQL数据库明明没有负载,CPU、硬盘、内存和网络等资源都很空闲,但很多SQL都pending在哪儿,MySQL数据库无法处理交易。这是怎么回事呢?
姚远OracleACE
2023/11/01
3490
为什么MySQL没有负载,但交易却跑不动?
相关推荐
Mysql 异常:Lock wait timeout exceeded; try restarting transaction的解决办法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验