RocketMQ消息中间件(六上)-订单系统的问题分析解答,MySQL中的binLog日志是什么?和大数据团队数据传输有什么关系呢?

  • Post author:
  • Post category:mysql

前言

在rocketMQ一中,分析了很多图文说明订单系统可能会遇到的一些问题,这次继续来分析和解决这些问题,或者是换句话讲,引入了MQ对叮当系统进行了哪些质一样的改变?

一个订单系统中存在的问题:

1、下单核心流程环节太多,性能比较差。
2、订单退款的流程可能面临退款失败的风险。
3、关闭过期订单的时候,存在扫描大量订单数据的问题。
4、跟第三方物流系统耦合在一起,性能存在抖动的风险。
5、大数据团队要获取订单数据,存在不规范直接查询订单数据库的问题。
6、做秒杀活动时候,订单数据库压力比较大。

1.下单核心流程环节太多,性能比较差

【先看下下单环节的图】
下单环节
每次支付完一个订单后都需要做同样的这些操作:

· 更新订单状态
· 扣减库存
· 增加积分
· 发放优惠券
· 发送短信
· 通知发货

导致的问题:一次核心链路执行时间过长,可能长达几秒钟!这不完犊子了,只要够快,穷就追不上我【=.=】

如何解决呢? 首先回想一下什么叫做异步,什么叫做同步,这个问题基本上在概念上来说就已经解决了。
【图看起来表达比较清楚】
在这里插入图片描述
解释下:
例如:更新订单状态需要30ms,扣减库存需要80ms,调用第三方短信需要200ms,调用仓储和发货500ms,促销系统200ms,积分系统200ms,还不算网络波动等其他的情况,整个链路执行完,客户要在页面转好几秒中的时间。
【优化后:更新订单状态30ms,扣减库存80ms,总共110ms就可以响应回去了,客户端是很爽的,其他的操作异步的从mq中获取数据执行自己要处理的业务逻辑,这样就不会影响到整个订单核心链路的性能了】
问题貌似得到了解决的方案,但是还存在很多细节问题:思考下,如果消息没有发送成功怎么办?发送成功了,消费者没有消费怎么办?消费者消费了,业务代码执行失败怎么办?要想代码写的好,必须要有洁癖,提前思考的准备,这些问题在后面陆陆续续都会得到一定的解答,但是这个也不算难,可以思考思考

这里贴段代码生产者和消费者是如何发送消息和消费消息的:

首先对之前的业务逻辑做一定的改动【也就是本文的图一】:
1.改造系统,订单系统除掉调用积分系统,促销系统,推送系统以及仓储系统的业务逻辑,改成发送一个消息到MQ中;
2.另外的积分系统、促销系统、推送系统和仓储系统都要从MQ中获取消息,根据消息来执行自己的业务逻辑;

producer生产者代码编写:
1.引入rocketMQ-client的依赖;
2.下面展示一些 生产者的内联代码片

// An highlighted block
public class RocktProducer{
//这个是rocket的生产者类,用这个生产者类发送消息到MQ
private static DefaultMQProducer producer;

static{
//构建一个producer实例对象
producer  = new DefaultMQProducer("order_producer_group");
//为producer设置nameServer的地址,可以拉取路由信息
//这样才知道每个topic的数据分散在哪些broker机器上
//然后此可以把消息发送到broker上去
producer.setNamesrvAddr("localhost:9876");
//启动一个producer
producer.start();
}

public static void send(String topic,String message) throws Exception{
//构建一条消息对象
Message msg = new Message{
topic,//这里就是制定发送消息到哪个topic上去
"",//这是消息的tag,后续再聊
message.getBytes(RemotingHelper,DEFAULT_CHARSET)//消息
}//利用producer发送消息
SendResult sendResult = product.send(msg);
System.out.print("%s%n",sendResult);
}
}

那么问题:

例如部署的集群,master有两台机器,那么此时消息会进入哪个master broker中去呢?
解答:topic是一个数据模型的逻辑上的概念,实际上存储的数据是分布式存储在多个master上面去的,意思就是“topicOrderPaySuccess”这个topic的数据会分散在两个master broker中,因此当发送一个订单消息过去的时候,会根据一定的负载均衡算法和容错算法将消息发送到一个broker中去,【问题铺垫:topic数据到底最终分散在哪几个topic,可以分散在多少个broker,producer到底是如何选择broker发送消息过去的?】
看一下原始图
socket长连接发送

其他系统改造为从RocketMQ中获取订单消息,然后根据获取到的消息执行对应的业务逻辑:
1.其他系统都引入mq相关的依赖;
2.下面展示一些 消费者内联代码片

public class RocketMQConsumer{
    public static void start(){
        try {
            //这里是rocketMQ消费者实例的对象
            //"credit_group"之类的就是消费者的分组
            //一般来说:例如积分系统就用"credit_consumer_group"
            //促销系统就用"marketing_consumer_group"
            //不同的系统取不同的名称
            DefaultMQPushConsumer consumer - new DefaultMQPushConsumer("credit_group");
            //这里是给消费者设置的nameServer的地址
            //可以拉取到路由信息。知道topic在哪些broker上
            //从对应的broker上拉取数据
            consumer.setNamesrvAddr("localhost:9876");

            //选择定于“topicOrderPaySuccess”的消息
            //这样会从这个topic的broker机器上拉取订单消息过来
            consumer.subscribe("topicOrderPaySuccess","*");

            //注册消息监听器来处理拉取到的订单消息
            //如果consumer拉取到了订单消息,就会回调这个方法处理
            consumer.registerMessageListener(new MessageListenerConcurrently(){
               public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
                               ConsumeConcurrentlyContext context){
                   //这里对获取到的msgs订单消息进行处理
                   //例如增加积分,发送优惠券,通知发货,等等
                   return ConsumeConcurrentlyStatus.COUSUME_SUCCESS;
               }
            });
            //启动消费者实例
            consumer.start();
            System.out.println("started.......");

            while (true){//别让线程退出,就让创建好的consumer不停的消费数据
                Thread.sleep(1000);
            }
        }catch (Exception exception){
            exception.printStackTrace();
        }
    }.start();
}

通过这段代码,积分系统和其他系统就可以从RocketMQ里消费“topicOrdeerPaySuccess”中的订单消息了,然后根据订单消息执行增加积分,发送优惠券,发送短信,通知发货的业务逻辑了;

汇总:那么第一个问题就得到了解决,整个订单系统,性能至少提升10倍往上,第一个问题得到解决的同时,第四个问题是否也得到了解决呢?

2、跟第三方物流系统耦合在一起,性能存在抖动的风险

这个问题在解决第一个问题的时候,这个问题也解决了,此时跟第三方是采取的消息传递,想想,我的核心业务逻辑执行完毕了,就会返回给客户端,其余的全都是异步执行的,至于第三方系统抖动也好,返回失败也罢,都没有影响到订单系统,只是需要做一个异常的处理,或者是回调;

在这里插入图片描述

3.大数据团队要获取订单数据,存在不规范直接查询订单数据库的问题

说白了,就是大sql,虽然基本上不会犯这种问题,可能有很多方式去应对大数据团队的数据获取,这里以MQ为例子;
3.1:首先看下影响《数据库裸奔的因果关系》:
数据库裸奔3.2:出现的问题:大数据团队的BI系统每天都会直接在订单系统数据库中执行上百次几百行的大sql,而且每次一个几百行大sql的执行都需要耗时几秒到几十秒不等,每次执行这样的大sql,都会导致mysql数据库服务器资源负载抖动,CPU,内存,磁盘Io的负载都瞬间升高。MySQL数据库资源瞬间升高,会导致订单系统在MySQL数据库上执行sql语句性能出现急速下降,因此订单系统的性能也会出现抖动。
3.3:解决方案:不让大数据团队直接从订单系统数据库中获取数据不就好了
3.4:这里啰嗦啰嗦整个系统的演变过程温馨提示:大神可跳
在这里插入图片描述
3.5:图上就是最简单的解决方案,把压力给到大数据团队的数据库中,只要不影响订单系统的运行即可【当然不是最优的】,那么考虑一个问题:单单一个订单支付成功的消息,能满足大数据团队的数据获取么?显然是不行的,大数据团队需要的是跟订单数据库一模一样的一份完整的数据,而不是仅仅是订单支付成功的消息,所以不能直接使用之前的TopicOrderPaySuccess这个topic的消息。
3.6:那么如何将完整的订单数据发送到rocketMQ中去?
实际上有一个比较简单的办法,就是再订单系统中但凡对订单执行增删改类的操作,就把这种对增删改的操作发送到RocketMQ中去。然后大数据团队的数据同步系统从rocketMQ中获取到订单增删改的操作,就会再自己的数据库中执行一样的增删改操作。通过还原执行一样的insert,update,delete语句,就可以在大数据的数据库中还原出一样的订单数据,这里是不是有点像redis中的AOF还原执行命令
继续改进
这种方案的问题就是订单系统为了将数据同步给大数据团队,必须再自己的代码里面耦合大量的代码去发送增删改操作到rocketMQ,这样会导致订单系统的代码出现严重的污染,因为这些发送增删改操作到rocketMQ中的代码是跟订单业务是没有关系的;

解决方案二:MySQL中的binLog日志同步出生

· MySQLbinLog同步系统,这种系统会监听MySQL数据库的binlog,所谓的binlog大致的可以理解为MySQL的增删改操作日志,如何MySQL binlog同步系统会将监听到的MySQLbinlog也就是增删改的操作日志发送给你的系统,让你来处理这些增删改;
· 市面上binlog同步系统:MySQL binlog系统现在有不少成熟的开源技术方案,例如阿里的canal,linkedin开源按钮的databus都可以监听到MySQL binlog,如何将MySQL binlog发送到系统,再去处理。
· 因此完全可以将数据同步方案修改为,采用canal简体你MySQL binlog,然后直接发送到rocketMQ中
大数据团队的数据同步系统从rocketMQ中获取到mysqlbinlog,也就获取到了订单数据库的增删改操作,接着将增删改操作还原到自己的数据库即可。
【继续改造】
在这里插入图片描述

为啥又加了什么人工只能团队,等等其他的团队?

订单系统的技术团队将完整的订单数据库的MySQL binlog推送到rocketMQ中,不管是大数据团队还是其他的技术团队,等等,只要想要订单数据,都可以直接从rocketMQ中获取完整的数据。大数据团队并没有必要只通过MySQL来做数据报表,也开源通过hadoop,spark,flink等大数据来出数据报表。
图有点多,来个小结
最完美的解决方案就是用canal、databus这样的MySQL binlog同步系统,监听订单数据库的binlog发送到rocketmq中,然后大数据团队的数据同步从rocketMQ中获取订单数据的增删改binlog日志,还原到自己的数据存储中去,开源是自己的数据库,也可以是hadoop之类的大数据生态技术,然后大数据团队将完整的订单数据还原到自己的数据存储中,就可以根据自己的技术能力出数据报表,不会再影响到订单系统的数据库;

顺水推舟,步步为营:

  1. 做秒杀活动时候,订单数据库压力比较大;
  2. 订单退款的流程可能面临退款失败的风险;
  3. 关闭过期订单的时候,存在扫描大量订单数据的问题;
    本文已经有些许知识点了,需要消化一下,而且内容太多,连看下去的勇气都缺乏,所以经历以简短分片的形势推送,猿友们MQ六下见—>链接: rocketMQ(六下)

版权声明:本文为weixin_46950473原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。