视频地址: https://www.bilibili.com/video/BV1PY4y1s7a1
一、场景
不知道你是否有无数次吐槽公司的架构设计,比如某一个关键的列表,
join
了无数张表 (join 代表了left join、 inner join 等)
做了一个新功能,新增了几张表,列表为了展示一个字段,又去关联一张大表,这样的操作,速度不慢才怪嘞
很遗憾我们现在的系统就是这样的,不知道你们有没有听说过ADB这样的数据库,它们主要是用来处理大数据的,性能超强,我们一个查询20s
的SQL,迁移到ADB里面那就0.Ns
了 (我们不去探索它为什么这么快哈)
但尽管如此,终会有一天ADB这样的数据库也扛不住,这一天已经不远了
以前我的想法总是说去重构代码,但每次只是想想就头痛,一个关键的列表,关联了十几张表,谁敢去动它呢?
二、解决方案
是不是一定没有办法解决呢?答案是否定的,最近我们有一个解决方案,并且已经实践了,一个20s的查询优化后可以达到0.Ns
怎么做呢?我们知道所有的数据最终都是写入MySQL数据库,那么我们就可以监控binlog
把十几张表里面我们需要用到的字段冗余到一张表里面(也就20个左右的字段),这样我们的查询就只需要 join一张表
join一次还是带了索引它能不快吗?
我们之所以关联了那么多张表不正是想要从它里面获取某个数据吗?假如我们关联A表是为了获取a1字段,那么我们是不是可以监控操作A表增、删、改的全部binlog,然后把每次操作都写入我们的宽表里面?是不是就做到了?
监控binlog的插件有很多,这里我推荐一个 MaxWell
,它是国外的一家公司开源用于监控binlog的软件,基于java开发的。
它使用起来很简单,完全和你的代码解耦,你单独的部署它,它可以把监控到的结果发送到 kafka、rabbitmq、redis等很多地方,然后你只需要去消费这个消息,对它进行业务处理即可。
三、实践
3-1、开始
下面我们来搭建一下maxwell,需要准备下面的环境
- JDK 11
- MySQL
- MaxWell
- RabbitMQ
- Linux/Mac 服务器
注:rabbitmq不是必须的,如果想要发送到rabbitmq里面则可以安装,默认是输出到控制台
JDK、MySQL、rabbitmq都自行安装,这里说一下,一般我们都已经安装好了JDK8,我们可以再下载一下JDK11,安装就行,不需要配置环境变量,到时候我们执行maxwell的时候指定JDK11 就好
3-2、下载maxwell
https://github.com/zendesk/maxwell/releases/download/v1.37.6/maxwell-1.37.6.tar.gz
它是一个解压即可用的软件,解压后目录如下:
我们需要把它上传到Linux服务器上,如果你是Mac电脑那么恭喜你,直接可以运行
3-3、MySQL配置
找到我们的 my.cnf
,添加上如下配置
# 给当前mysql一个唯一id
server_id=1
# 开启binlog
log-bin=master
binlog_format=row
binlog_format 是设置binlog的格式,它有三种格式
- statement 基于SQL语句的复制
- row 基于行的复制(row-based replication, RBR)
- mixed 混合模式复制(mixed-based replication, MBR)
详情参考 MySQL日志篇,MySQL日志之binlog日志,binlog日志详解
3-4、配置文件
把下面的配置文件放在 bin
目录下, 配置文件都很简单,相信你能看懂
# tl;dr config
log_level=info
# mysql login info
host=127.0.0.1
port=3306
user=root
password=123456
# 结果打印在控制台
producer=stdout
# 结果输出到rabbitmq
#producer=rabbitmq
#rabbitmq_host=127.0.0.1
#rabbitmq_port=5672
#rabbitmq_user=guest
#rabbitmq_pass=guest
#rabbitmq_virtual_host=/
#rabbitmq_exchange=maxwell
#rabbitmq_exchange_type=fanout
#rabbitmq_exchange_durable=false
#rabbitmq_exchange_autodelete=false
#rabbitmq_routing_key_template=%db%.%table%
#rabbitmq_message_persistent=false
#rabbitmq_declare_exchange=true
3-5、修改启动文件
启动文件是 bin/maxwell
,因为大部分人配置都是JDK8,所以直接从环境变量里面读取是不行的,这里我们要把JDK换成11的
找到你JDK11的安装目录,把下面的代码删掉换成你自己的JDK11位置,比如我的配置如下
JAVA="/usr/local/jdk-11.0.15.1/bin/java"
3-6、启动
进入到bin目录下,输入下面的命令
./maxwell
有可能提示权限不够,我们可以使用下面的命令赋值权限后,再启用
chmod 777 ./maxwell
3-7、测试
我们可以使用navicat或者任意操作数据库的办法,比如我们在数据库里面执行这样一句
INSERT INTO user (id, user_name) VALUES (7, '小道仙');
我们的控制台会打印这样的数据(数据是一行的,为了展示,我把它换行了)
{
"database": "test",
"table": "user",
"type": "insert",
"ts": 1654417263,
"xid": 70955,
"commit": true,
"data": {
"id": "7",
"user_name": "小道仙"
}
}
更新语句
UPDATE `user` SET user_name = '小道仙97' WHERE id = 7;
{
"database": "test",
"table": "user",
"type": "update",
"ts": 1654417405,
"xid": 71147,
"commit": true,
"data": {
"id": "7",
"user_name": "小道仙97"
},
"old": {
"user_name": "小道仙"
}
}
删除语句
DELETE FROM `user` WHERE id IN(1,2);
{
"database": "test",
"table": "user",
"type": "delete",
"ts": 1654417609,
"xid": 71478,
"xoffset": 0,
"data": {
"id": "1",
"user_name": "张三"
}
}
{
"database": "test",
"table": "user",
"type": "delete",
"ts": 1654417609,
"xid": 71478,
"xoffset": 1,
"data": {
"id": "2",
"user_name": "李四"
}
}
四、其它
4-1、maxwell 官方文档
4-2、实际使用中的问题
其实也就是一些队列消费的问题了,比如保持幂等
、消息丢失
、消费速率
、消息积压
问题等,这里主要说一下消息积压的问题。
我们开了40个线程,消费队列5k,但依旧消费不过来,线程池默认的拒绝策略是丢弃并抛出异常,我们可以改成使用当前线程处理,这样虽然消费会慢点,但不至于丢数据。 (当然实际上开多少个线程还是要根据你的业务来)
maxwell是监控当前链接的全部数据库,我们可以进行一个限制,使其只监控我们需要的库、表。
ps:这里吐槽一下,线程池的拒绝策略我背了很熟了,但出了问题我却没想到可以换一种策略来处理。
4-N、下面是准备的安装包
关注微信公众号回复关键字获取:maxwellFile