[转]mysql binlog in realtime
原文:http://guweigang.com/blog/2013/11/18/mysql-binlog-in-realtime/
众所周知,MySQL是最受欢迎的互联网数据库(没有之一)———————为开源而生。发展初期,很多公司都受益于其易用性和经济性。随着这些公司的成长,越来越多的公司投入到MySQL的开发中,因此MySQL的特性也越来越丰富,如:不同特性的存储引擎、Binlog主从复制方案等。
今天我们要探讨的就是如何实时解析MySQL Binlog,以及其所带来的巨大的业务价值。我可以在一瞬间说出很多应用场景,如:主从复制、缓存系统、检索引擎等。如下图所示,MySQL的Binlog不仅仅能用于MySQL服务之间的主从复制,还能造福其他非MySQL服务:

在一个成熟的商业系统中我们有且仅有一份正确的全量数据(这里指MySQL中的数据)。而面对现在应用丰富的功能以及用户越来越高的要求,MySQL并不能一直很好的满足我们。比如:我们需要使用缓存系统来加速服务,需要完备的检索系统来提供搜索服务,需要事件触发实时地发送通知等。但是摆在我们面前的问题是只有一份数据,且我们直接操作的都是MySQL(本文用MySQL来代表所有数据库系统),如何能让除MySQL之外的其他系统拥有同样的数据?且我们总是希望MySQL中的数据与其他系统的数据差异越小越好,越实时越好。当然我们可能会有一系列方案来解决这个问题,最典型的方法有两种:
- 双写, 当你在更新MySQL的同时更新缓存系统、更新检索引擎,触发事件发送通知。这么做的确能解决问题,但是因为都是异构系统,没有一种机制能够保证写入成功。也就是说随着时间的推移,数据差异会越来越大。不仅如此,如果外部系统很多,这么做还会影响到主业务逻辑的响应时间。
- 定时任务,在主业务逻辑中耦合更新其他系统的代码是不OK的,外部系统只有几个还行,如果要更新100个外部系统,这时候当如何处理呢?通过定时任务异步地去做这件事情看上去是一个更好的方案。的确如此,但是任务执行频率是多少呢?每1分钟?每10分钟?当然我们可以根据业务需要制定这个频率,乍想一下没啥问题,不过再想一下,定时任务具体用来做什么呢?
- 定时导出全量:在数据量比较少的时候这是最省事儿的方法,当然下游每次必须清空自己的数据重新全量导入,数据量稍大一点代价就特别大。
- 业务系统维护一个队列(在业务库中新建表当作队列):在业务系统中操作数据的时候顺便把自己操作的日志写到队列里面,然后由下游来消费这个队列。
这种方式由业务系统来维护操作记录,好处是能保证数据的业务完整性,坏处是在业务端耦合了非业务相关的逻辑,每当数据有变更时都需要开启事务保证业务操作和其操作日志都能正确地写入。如果以后因为某些原因要直接操作DB,这种情况就很危险了,在操作DB时无法模拟复杂的业务逻辑计算和关系。
- 定时扫表找出变更记录:每条数据都会有一个updateTime (on update CURRENT_TIMESTAMP)字段,当该条记录被修改时,MySQL会隐式地更新这个字段为当前时间。这种方法使业务端远离水火,以一种更解耦的方式来处理增量,非常适合无状态缓存更新之类的场景,当然如果表记录数过大可能会有慢查询。但如果存在状态流转的的数据,这种方式会丢失状态流转方向。
如果你使用队列方式,很可能会写出下面这段代码:
$db = getDI()->get("test_db");
$db->begin();
$db->update("// logic operate");
$db->insert("// operate logs");
$db->commit();
而且很可能你设计的操作日志表看起来会是这样子的:
| event_id | user_id | object_id | level | event_type | mcid | addtime |
| 152 153 154 |
48 48 48 |
3007209739 3007209739 3007209739 |
200 202 201 |
23 24 24 |
0 0 0 |
2013-07-17 04:06:05 0000-00-00 00:00:00 0000-00-00 00:00:00 |
如果你扫表的话可能会写出下面这段SQL:
SELECT * FROM `products` WHERE updateTime > '2013-11-12 12:00:00' ORDER BY updateTime LIMIT 1000;
下面我来具体介绍下我们的应用场景,以及我们是如何利用MySQL Binlog解决这个问题的,先上图:

这是我们整个系统的架构图,左边可以看成用户系统,右边则是商业系统。浏览器向前端搜索服务集群(图中红色部份)发送请求后,前端集群会把用户的请求数据发往很多个后端服务,而不同的后端之间并不是同构的,所以基本上都会有一个“长连接/协议转换代理层”(图中红色部份),然后再由后端决定是否满足用户需求,最后再由前端集群拼接来自不同后端服务的结果并呈现给网民。
看似一个海量访问的服务,其实是由无数个小服务组成的,图中所示就是我们组做的小服务,在具体场景中蓝色部分做的事情是把前端服务集群的协议转换成FASTCGI协议,并转发给后端服务UI/PHP-FPM。虽然名称叫UI,但是做的不是我们所理解的UI的工作,而是对于后面的检索和数据流来说它是最前面的一部分,所以才叫UI。它承担的工作是解析代理层发来的数据,并根据请求数据和参数构造结构化的查询条件,当然这个构造的过程可能仍需要请求很多外部服务,然后向检索系统(BS)发出请求,BS返回的是一个实体ID集合(你可以理解为商品ID),UI根据实体ID到数据库(这里省略了缓存系统)里面查询实体详情,整个完程就是这样。
我们这个架构中也存在上面描述的异构系统,BS(Basic Search)模块是对Lucene的二次开发,它需要DB中的数据来建立索引,而客户会时不时地往DB里面写一些数据,为了能让UI实时地从BS中检索到新数据,DB的所有变更要能实时地体现到BS中,这是用双写/定时任务无法完成的。所以我们开发了基于MySQL Binlog的异步事件框架(AdPipe)。
AdPipe由以下几部分组成:
- BIZ framework: 大家都知道PHP是最快的,所以经常需要变更的事件逻辑使用PHP写。
- PHP ext: 为了能在PHP用户空间写Binlog事件逻辑,不可避免地需要PHP扩展。
- Binlog listener: 与MySQL Server连接,实现Binlog议协。
来看看BIZ框架的代码:
首先是启动脚本
上面代码主要职责是连接MySQL Server,并且设置Binlog的位置,建立连接后根据接收到的事件调用BinlogEvent相应的方法。而BinlogEvent所有方法仅仅返回一些状态码(IGNORE, SUCCESS, L_EXIT, SIGNAL_QUIT),这些状态码控制着消息该如何处理。
其次是事件处理类,BinlogEvent:
看171行,getHandleClass方法通过获取一个业务对象来处理事件,如果没有找到处理某个DB事件的类,那么默认会调用DefaultEvent来处理。
请看DefaultEvent类:
由于Binlog事件和DB的scheme是息息相关的,所以在DefaultEvent中也调用了Model类来获取表字段。每个事件处理类都有三个方法,onWrite, onUpdate, onDelete,你可以在方法中获取DB变更的数据,然后根据业务需求做各种变换,然后打包成消息。
接下来就说到消息类,这里的消息是指逻辑消息,是经过业务代码变换后准备发往BS的消息,Message
以上就是AdPipe系统中biz framework的代码,当然代码要能运行,需要安装binlog listener和php-binlog扩展,请点击下面链接查看:
- PHP Binlog: https://github.com/BullSoft/php-binlog
- MySQL Replication Listener: https://github.com/BullSoft/mysql-replication-listener
具体安装过程可以分别查看它们的readme。
如果biz framework挂了,会影响其他系统吗?
这是个好问题,如果biz framework挂了,我们也不用担心,因为在我们的架构中AdPipe的上游和下游都是队列,上游是binlog队列,而且文件存在MySQL服务器上:
| filename | position |
| mysql-bin.000001 mysql-bin.000002 ... |
422655739 124544114 ... |
而我们的下游是AMQ之类的消息队列,BS通过AMQ来获取消息。上下游均通过队列解耦,所以biz framework不幸挂了,不会影响其他系统。
如果biz framework挂了,能快速恢复运行吗?
当然可以,biz framework每次处理完事件后,都会保存该事件的filename和position,所以如果服务挂掉,可以在连接MySQL服务器之后显示地设置filename和position,如:binlog_set_position($link, $filename, $position)。
下游(BS)如果挂了,能快速恢复吗?
这是个非常关键的问题,如果BS挂了它需要重建索引。随着系统的运行,增量数据越来越多,如果给BS一份最原始的基准基量文件,那么BS要消费从系统启动至今的所有增量消息,这可能需要几个月甚至一年。如果我们有一份最新的全量基准文件,情况就大不一样了,所以AdPipe另一个功能是定时生成最新的全量基准文件,以供下游恢复数据使用,这样下游只需追有限的增量消息便能跟上DB的数据。
所以这进一步引发我们对这个事情的思考,能不能设计一个通用的事件触发系统呢,这里我们只考虑 文件(inotify)/MySQL(binlog)/MongoDB(oplog),其他类似于redis也有keyspace notifications功能。

[转]mysql binlog in realtime的更多相关文章
- Canal - 数据同步 - 阿里巴巴 MySQL binlog 增量订阅&消费组件
背景 早期,阿里巴巴 B2B 公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求 ,主要是基于trigger的方式获取增量变更.从 2010 年开始,公司开始逐步尝试数据库日志解析,获取增量变 ...
- 原创工具binlog2sql:从MySQL binlog得到你要的SQL
从MySQL binlog得到你要的SQL.根据不同设置,你可以得到原始SQL.回滚SQL.去除主键的INSERT SQL等. 用途 数据回滚 主从切换后数据不一致的修复 从binlog生成标准SQL ...
- MySQL binlog中的事件类型
MySQL binlog记录的所有操作实际上都有对应的事件类型的,譬如STATEMENT格式中的DML操作对应的是QUERY_EVENT类型,ROW格式下的DML操作对应的是ROWS_EVENT类型. ...
- MySQL Binlog Mixed模式记录成Row格式
背景: 一个简单的主从结构,主的binlog format是Mixed模式,在执行一条简单的导入语句时,通过mysqlbinlog导出发现记录的Binlog全部变成了Row的格式(明明设置的是Mixe ...
- MySQL binlog的格式解析
我搜集到了一些资料,对理解代码比较有帮助. 在头文件中binlog_event.h中,有描述 class Log_event_header class Log_event_footer 参见[Myst ...
- Mysql binlog
理解Mysql binlog 日志的三种模式 本文介绍下,mysql中binlog日志的三种模式,了解了各种模式的不同之处,才能更好地应用.有需要的朋友建议参考下. 一,模式1 Row Lev ...
- MySQL bin-log 日志清理方式
MySQL bin-log 作用 1.数据恢复:如果你的数据库出问题了,而你之前有过备份,那么可以看日志文件,找出是哪个命令导致你的数据库出问题了,想办法挽回损失. 2.主从服务器之间同步数据:主 ...
- Mysql Binlog 三种格式介绍及分析
一.Mysql Binlog格式介绍 Mysql binlog日志有三种格式,分别为Statement,MiXED,以及ROW! 1.Statement:每一条会修改数据的sql都会记录在 ...
- Mysql binlog日志解析
1. 摘要: Mysql日志抽取与解析正如名字所将的那样,分抽取和解析两个部分.这里Mysql日志主要是指binlog日志.二进制日志由配置文件的log-bin选项负责启用,Mysql服务器将在数据根 ...
随机推荐
- NHibernate系列文章六:NHibernate数据类型映射
摘要 NHibernate支持所有的数据库数据类型. 以SQL Server数据库为例,下表是NHibernate支持的SQL Server数据库最常见的数据类型对照表. 第一列是NHibernate ...
- jafka消息结构
- cocos2d-x 坐标系
OPenGL坐标系:原点为屏幕左下角 屏幕坐标系:原点在屏幕左上角
- mysql 数据库导入 导出,解决 导入 错误问题
mysqldump -uxxxx -pxxxx -hrds2383jse53pi6ipwmf.mysql.rds.aliyuncs.com legaokao > /root/legaokaodu ...
- tomcat各种问题汇总
1. 让Tomcat支持中文路径名和中文文件名 因为内置get协议中的URL编码都是ISO-8859-1,所以需要我们强制编码,在tomcat/conf/Server.xml中添加URIEncodin ...
- Python全栈--9.1--面向对象进阶-super 类对象成员--类属性- 私有属性 查找源码类对象步骤 类特殊成员 isinstance issubclass 异常处理
上一篇文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中) 对象 ...
- ios9 http请求不能使用
为了跟新新版本的ios9版本,使用http请求时会碰到无法加载数据的情况 App Transport Security has blocked a cleartext HTTP (http://) r ...
- AngularJS学习--- 动画操作 (Applying Animations) ngAnimate step 12
1.切换目录 git checkout step-12 npm start 2.效果图 这里在点击右边的缩略图时,会有一个很明显的从下向上的动画过程. 3.代码实现: step11和step12之间的 ...
- java核心知识点学习----多线程并发之线程同步
1.什么是线程同步? 多线程编程是很有趣的事情,它很容易出现"错误情况",这种情况不是由编码造成的,它是由系统的线程调度造成的,当使用多个线程来访问同一个数据时,很容易出现&quo ...
- sbt的assembly插件使用(打包所有依赖)
1.sbt是什么 对于sbt 我也是小白, 为了搞spark看了一下scala,学习scala时指定的构建工具就是sbt(因为sbt也是用scala开发的嘛),起初在我眼里就是一个maven(虽然ma ...