如何将数亿MySQL数据无缝迁移到MongoDB?

2020 年 11 月 17 日

如何将数亿MySQL数据无缝迁移到MongoDB?

在好大夫在线内部,S3系统负责各业务方操作日志的集中存储、查询和管理。目前,该系统日均查询量数千万次,插入量数十万次。随着日志量的不断累积,主表已经达到数十亿,单表占用磁盘空间400G+。S3是业务早期就存在的系统,当时为了简单快速落地,使用了Mysql来存储,随着业务的不断增长,同时也要兼顾性能和可扩展性,到了必须要重新选型的时候了。


新项目命名为:LogStore。

目标

1、安全性


S3 系统在设计之初,没有按业务系统考虑数据隔离,而是直接采用 key(系统 + 类名 + id) + 有限固定字段 + 序列化 value 的方式进行存储,这种方式显然不便于后续集群拆分和管理。LogStore 系统要在逻辑上进行数据区域划分,业务方在接入时要指定 app 进行必要的权限验证,以区分不同业务数据,进而再进行插入和查询操作。

2、通用性


S3 主要提供一种 3 层结构,采用 MySQL 固定字段进行存储,这就不可避免的会造成字段空间的浪费。LogStore 系统需要提供一种通用的日志存储格式,由业务方自行规定字段含义,并且保留一定程度的可查询维度。

3、高性能


S3 系统的 QPS 在 300+,单条数据最大 1KB 左右。LogStore 系统要支持当前 QPS 10 倍以上的写入和读取速度。

4、可审计


要满足内部安全审计的要求,LogStore 系统不提供对数据的更新,只允许数据的插入和查询。

5、易扩展


LogStore 系统以及底层存储要满足可扩展特性,可以在线扩容,满足公司未来 5 年甚至更长时间的日志存储需求,并且要最大化节省磁盘空间。


方案选型


为了达成改造目标,本次调研了四种存储改造方案,各种方案对比如下:



1、我们不合适—分库分表


分库分表主要分为应用层依赖类中间件和代理中间件,无论哪种均需要修改现有 PHP 和 Java 框架,同时对 DBA 管理数据也带来一定的操作困难。为了降低架构复杂度,架构团队否定了引入 DB 中间件的方案,还是要求运维简单、成本低的方案。

2、我们不合适—TiDB


TiDB 也曾一度进入了我们重点调研对象,只是由于目前公司的 DB 生态主要还是在 MGR、MongoDB、MySQL 上,在可预见的需求中,也没有能充分发挥 TiDB 的场景,所以就暂时搁置了。

3、我们不合适—ElasticSearch


ELK-stack 提供的套件确实让 ES 很有吸引力,公司用 ES 集群也有较长时间了。ES 优势在于检索和数据分析领域,也正是因为其检索和分析的功能的强大,无论写入、查询和存储成本都比较高,在日志处理的这个场景下,性价比略低,所以也被 pass 了。

4、适合的选择—MongoDB


业务操作日志读多写少,很适合文档型数据库 MongoDB 的特点。同时,MongoDB 在业界得到了广泛的使用,公司也有很多业务在使用,在 MongoDB 上积累了一定的运维经验,最终决定选择 MongoDB 作为新日志系统存储方案。


性能测试


为了验证 MongoDB 的性能能否达到要求,我们搭建了 MongoDB 集群,机器配置、架构图和测试结果如下:

1、机器配置


MongoDB 集群 3 台机器配置如下:

2、架构图

3、测试场景


本次 MongoDB 测试采用 YCSB(https://github.com/brianfrankcooper/YCSB)性能测试工具,ycsb 的 workloads 目录下保存了 6 种不同的 workload 类型,代表了不同的压测负载类型,本次我们只用到了其中 5 种,具体场景和测试结果如下。


(1) 插入平均文档大小为 5K,数据量为 100 万,并发 100,数据量总共 5.265G 左右,执行的时间以及磁盘压力

结论:插入 100w 数据,总耗时 219s,平均 insert 耗时 21.8ms,吞吐量 4568/s


(2) 测试 90%读,10%更新,并发 100 的场景

结论:总耗时 236s,read 平均耗时 23.6ms, update 平均耗时 23.56ms,吞吐量达到 4225/s


(3) 测试 读多写少,100%读 ,并发 100 场景

结论:总耗时 123s,平均 read 耗时 12.3ms,吞吐量达到 8090/s


(4) 测试读多写少,90%读,10%插入,并发 100 的场景

结论:总耗时 220s,read 平均耗时 21.9ms,insert 平均耗时 21.9ms,吞吐量达到 4541/s


(5) 测试 混合读写,50%读,25%插入、25%更新,并发 100  的场景

结论:总耗时 267s,read 平均耗时 26.7ms,update  平均耗时 26.7ms,insert  平均耗时 26.6ms ,吞吐量 为 3739/s

4、测试结果对比



可以看出 mongodb 适合读多写少的时候,性能最好,读写速率能满足生产需求。

无缝迁移实践

第一步:系统应用层改造+LogStore 系统搭建


首先,在 S3 系统中内置读开关和写开关,可将读写流量分别引入到 LogStore 系统中,而新应用的接入可以直接调用 LogStore 系统,此时结构示意图如下:

第二步:增量数据同步


为了让 S3 系统和 LogStore 系统中新增数据达到一致,在底层数据库采用 Maxwell 订阅 MySQL Binlog 的方式同步到 MongoDB 中,示意图如下:



Maxwell(http://maxwells-daemon.io)实时读取 MySQL 二进制日志 binlog,并生成 JSON 格式的消息,作为生产者发送给 Kafka,Logstore 系统消费 Kafka 中的数据写入到 mongodb 数据库中。


至此,对于业务方现有日志类型,新增数据在底层达到双写目的,S3 系统和 LogStore 系统存储两份数据;如果业务方新增日志类型,则直接调用 LogStore 系统接口即可。接下来,我们将对已有日志类型老数据进行迁移。

第三步:存量数据迁移


此次迁移 S3 老数据采用 php 定时任务脚本(多个)查询数据,将数据投递到 RabbitMQ 队列中,LogStore 系统从 RabbitMQ 队列拉取消息进行消费存储到 MongoDB 中,示意图如下:


(1) 由于原 mysql 表中 id 为 varchar 类型并且非主键索引,只能利用 ctime 索引分批次进行查询,数据密集处进行 chunk 投递到 mq 队列中。


(2) 数据无法一天就迁移完,迁移过程中可能存在中断的情况。脚本采用定时任务每天执行 20h, 在上线时间停止执行,同时将停止时间记录到 Redis 中。


(3) 由于需要迁移数据量较大,在 mq 和消费者能承受的情况下,尽可能多地增加脚本数量,缩短导数据的时间。


(4) 脚本执行期间,观察业务延时情况和 MySQL 监控情况,发现有影响立即进行调整,以保障不影响正常业务。

第四步:校验数据


老数据导入完成后,下面就要对老数据进行校验,校验从两个方面进行: 数据量和数据完整性。


数据量:基于 S3 系统老数据的 id, 查询在 MongoDB 中是否存在,如果不存在则进行补偿重发。


数据完整性:对于 S3 和 MongoDB 中的数据按照相同规则进行 md5 校验,校验不通过则进行补偿重发。


第五步:数据双写


将应用层预制的写开关打开,将流量导入到 LogStore 中,此时 mysql 的流量并没有停掉,继续执行 binlog 同步。结构如下:


从图中可以看到,从 S3 调用点的写接口的流量都写入到 MongoDB 数据库 backuplogs 集合中,为什么不直接写入到 logs 表中呢?留个小悬念,在后文中有解释。

第六步:灰度切换 S3 读到 LogStore 系统

上文我们提到,对于 S3 系统应用层读写调用点均分别内置了切换开关,打开应用层读开关,所有的读操作全部走 LogStore, 切换后示意图如下所示:


第七步:灰度切换写接口到 LogStore 系统

打开应用层写开关,所有写操作会通过 mq 异步写到 MongoDB 中,那如何证明应用层写调用点修改完全了呢?


上文中双写数据一份到 logs 表中,一份到 backuplogs 表中,通过 Maxwell 的 Binlog 同步的数据肯定是最全的,数据量上按理来说 count( logs) >= count(backuplogs), 如果两个集合一段时间内的数据增量相同,则证明写调用点修改完全,可以去掉双写,只保留 LogStore 这条线,反之需要检查修改再次验证。切换写完成后,示意图如下:


MongoDB 与故障演练


故障演练能够检测服务是否真正高可用,及时发现系统薄弱的环节,提前准备好预案减少故障恢复时间。为了验证 MongoDB 是否真正高可用,我们在线下搭建了 MongoDB 集群:



同时,我们编写脚本模拟用户 MongoDB 数据插入和读取,基于好大夫在线自研故障演练平台,对机器进行故障注入,查看各种故障对用户的影响。故障演练内容 CPU、内存、磁盘、网络和进程 Kill 等操作,详情如下图所示:



实验结果:

1、CPU、磁盘填充和磁盘负载对 MongoDB 集群影响较小。


2、内存满载可能会发生系统 OOM,导致 MongoDB 进程被操作系统 Kill,由于 MongoDB 存在数据副本和自动主从切换,对用户影响较小。


3、网络抖动、延迟和丢包会导致 mongos 连接服务器时间变长,客户端卡顿的现象发生,可通过网络监控的手段监测。


4、分别主动 Kill 掉 MongoDB 的主节点、从节点、仲裁节点、mongos、config 节点,对整个集群影响较小。


整体而言,MongoDB 存在副本和自动主从切换,客户端存在自动检测重连机制,单个机器发生故障时对整体集群可用性影响较小。同时,可增加对单机器的资源进行监控,达到阈值进行报警,减小故障发现和恢复时间。

总结


1、MongoDB 的使用

  • MongoDB数据写入可能各个分片不均匀,此时可以开启块均衡策略;由于均衡器会增加系统负载,最好选择在业务量较小的时候进行;

  • 合理选择分片键和建立索引,会使你的查询速度更快,这个要具体场景具体分析。


2、迁移数据

  • 必须保留唯一标识数据的字段,最好是主键id,方便校验数据;

  • 一定要考虑多进程,脚本要自动化,缩短迁移时间和减小人工介入;

  • 迁移过程中,要时刻关注数据库、中间件及应用相关指标,防止导出导入数据影响正常业务;

  • 要在同样配置的环境下充分演练,提前制定数据比对测试用例,以防止数据丢失;

  • 每一步线上操作(如切换读写),都要有对应的回滚计划,最大限度降低对业务的影响;


作者介绍:孙文华,好大夫在线系统开发工程师,专注于监控系统、缓存系统和公共基础系统开发,对容器化和文件存储也有涉猎和研究。

2020 年 11 月 17 日 15:011582

评论 12 条评论

发布
用户头像
感谢分享,学到了
2020 年 11 月 17 日 15:33
回复
用户头像
很棒的技术文章,满满的干货
2020 年 11 月 17 日 15:31
回复
用户头像
学习一下
2020 年 11 月 17 日 15:22
回复
用户头像
有内容啊,很丰富啊
2020 年 11 月 17 日 15:20
回复
用户头像
感谢楼主为我们迁移数据提供了一种新思路,maxwell思路可以
2020 年 11 月 17 日 15:20
回复
用户头像
可以看到文字内容了
2020 年 11 月 17 日 15:19
回复
用户头像
文章是否有点水?
2020 年 11 月 10 日 09:40
回复
通篇的这个不合适那个不合适,没见到切实的不合适的理由,目测也就是“mongodb我比较熟/比较喜欢,所以比较稳”所以才这么决定的吧~
2020 年 11 月 18 日 09:25
回复
熟和稳 这个理由已经很充分了。
2020 年 11 月 20 日 19:19
回复
用户头像
就标题和图,没有内容。
2020 年 11 月 10 日 09:36
回复
用户头像
这....文字内容呢?

2020 年 11 月 10 日 09:07
回复
用户头像
看不到文章
2020 年 11 月 10 日 09:06
回复
没有更多评论了
发现更多内容

物联网方面的竞赛有那些?

IoT云工坊

多币种钱包app系统开发,数字货币交易所系统源码开发

系统开发咨询:I76-883I-5I52 邓森

架构师训练营大作业(一)

木头发芽

秋招末声大厂社招面经:蚂蚁金服、拼多多、字节跳动(现已入职蚂蚁)

Java成神之路

Java 程序员 架构 面试 编程语言

2020年物联网行业有哪些新趋势

IoT云工坊

Dubbo 3.0 前瞻系列:服务发现支持百万集群,带来可伸缩微服务架构

阿里巴巴云原生

开源 微服务 云原生 dubbo 中间件

联联周边游系统源码

Geek_a620db

译|Optimal Logging

cyningsun

监控 日志 异常 故障 错误

任务调度框架Quartz快速入门

程序员小毕

Java 架构 分布式 框架 quartz

Seata-AT 如何保证分布式事务一致性

阿里巴巴云原生

云计算 开源 分布式 微服务 云原生

最新蚂蚁五面、拼多多三面、字节四面(已入职拼多多),个人面经分享

Java成神之路

Java 程序员 架构 面试 编程语言

阿里云仓库使用小技巧

Java maven

搜狗开源框架发布纯自研C++ Kafka客户端

Geek_459987

PiNetwork 挖矿算力系统开发

Geek_a620db

STARFIELD星域APP系统开发|STARFIELD星域软件开发

开發I852946OIIO

系统开发

区块链电子合同铸就数字经济信任基石

CECBC区块链专委会

电子合同

批量作业调度工具Taskctl Web应用版/ETL免费调度工具/数据挖掘,抽取,转换工具

会飞的鱼

数据挖掘 大数据处理 kettle 运维自动化 海豚调度

高速增长的跨境电商业务背后,区块链应用场景来了吗?

CECBC区块链专委会

跨境电商

7年Java开发经验,面试20多家公司,砍下16个Offer,总结干货面试题!

Java架构追梦

Java 架构 面试 面试题 大厂

vivo 互联网业务就近路由技术实战

vivo互联网技术

中间件 服务器 分布式路由

物联网目前的安全问题有哪些?

IoT云工坊

盘点2020 | 作为技术号主的一年!

小傅哥

Java 小傅哥 盘点2020 技术成长 技术盘点

为什么线程安全的List推荐使用CopyOnWriteArrayList,而不是Vector

Java旅途

Java List 线程安全 vector

架构师训练营大作业 (二)

木头发芽

LTN挖矿系统开发

Geek_a620db

vivo 全球商城:订单中心架构设计与实践

vivo互联网技术

分库分表 服务器 架构设计

HBC环保卫士系统搭建

Geek_a620db

如何基于SDK快速开发一款IoT App控制智能灯泡(Android版)

IoT云工坊

android App 物联网 API sdk

智慧公安大数据可视化分析平台开发,重点人员管控系统

WX13823153201

企业使用云计算低效益怎么办?区块链或成良药

CECBC区块链专委会

云计算

漏洞扫描软件AWVS的介绍和使用

行者AI

安全 漏洞

如何将数亿MySQL数据无缝迁移到MongoDB?-InfoQ