mysql100亿海量数据迁移

最近对用户关注关系进行了重构,涉及到代码由C改为PHP模块维护,cache重建以及本文要着重讲的海量用户关注数据迁移。虽然早就确认好了方案,但是实施起来要远比想象中的难搞,大鹏说过 “在难搞的日子里笑出声来”,不过我和dba委实笑不出来…

背景

目前业务用户关注关系模块存在几个问题:

  • 用户关注关系目前仍采用的是C模块,模块的开发、维护成本略高
  • 老的数据库采用客户端取模分表,几乎无扩展性可言,目前单表存储约2.5亿数据,数据读写的qps比较高,且表结构采用的联合主键,对接口性能有一定影响。所以后续计划采用分布式数据库,分表由原来20张表改为256表。
  • 数据量太多,用户关注信息双份存储,比如:A关注B,会存到两类表中,即关注表和被关注表,两类表的表结构完全相同(fromid,toid,followtime),只是索引不同,关注表的索引建立在fromid,用于查询用户A关注了哪些用户。被关注表的索引建立在to_id,用于查询用户A被哪些用户关注。目前两类表总计100亿条数据。
  • 业务数据冗余,由于历史原因,两类表中存储的fromid、toid并非用户的userid,而是建立了一层映射outerid,用于对外展示,所以内部请求用户A(userid:a)获取所有关注用户,需要先转换成outerid:a,然后获取所有关于用户后,将outerid在转换成userid,平白增添了2次冗余查询。重构后计划采用user_id直接关联。

综上我决定了重构,废弃掉先用的数据存储方案,由客户端维护的mysql分库分表模式变更为存储到分布式数据库ddbs,重构的最大难题就是这100亿数据的准换、迁移。

ddbs

:公司内部利用zookeeper以及自开发的dbproxy实现的一种分布式数据库:

  • 支持数T存储
  • 32倍单机写入性能
  • 分表对业务透明
  • 扩容对业务无感知
  • 成熟的容灾体系

难度

  • 线上服务不能受影响
  • 迁移数据量巨大,保证数据快速迁移以及数据完整性、一致性

思路

归纳下,主要有两点工作:

  1. 数据从outerid维度转换为userid维度
  2. 100亿数据由mysql转移到分布式数据库

思路:

如果保证线上服务不受影响,又要数据完整全部写入,最好的方法就是将数据流在某一刻分割,之前的数据定义为历史数据,之后数据定义为新增数据,并将新增数据能另外保存一份,我们称这一时刻为关键时刻….

所以我们引入消息队列(rabbitMq、kafka等),将数据双写,一方面更新现在数据库继续提供服务,另一方面,将数据写入消息队列,并阻塞队列,在将历史数据迁移完成之后再消费队列消息。

方案

  • 代码重构上线,线上维护两套逻辑,原有C模块继续接流量,新的php模块不承接流量
  • 将原来上层接口直接调用C模块写DB修改为先写入消息队列异步处理,新建两个pusher,一个pusher接C模块,一个pusher接PHP模块
  • 某一时刻将两个pusher同时停止,并记录C模块停止时pusher的消息命令点,以此命令点即为关键时刻。
  • pusher关闭后,则数据库没有新的写入了,下掉一个从库,关闭此从库的主从同步,则从库的数据为历史数据。然后开启C模块的pusher,php模块的pusher继续关闭。
  • 将从库数据导入Hive库,利用hive将数据从outerid的关联维度变更为重构后我们需要的userid维度的数据。
  • 开启64个进程、单进程每次批量写入1000行数据,大约2天历史数据写入完毕
  • 新增数据在历史数据灌入完成后开启新增的pusher ,进行新增数据导入
  • 数据组数据导出完成后,下掉的从库开始追主库进行主从同步,在无主从延迟后提供服务
  • 验证两个数据库数据同步后,继续双写,并开启小流量,验证重构后功能,无误后逐机房切换流量

总结

  • 本次项目进展整体比较顺利,但在和dba、op沟通上不充分,方案虽然都理解但是细节上把控不到位,导致数据在方案开始实施时进展缓慢,线上pusher预计停5分钟,结果由于沟通细节停了30分钟
  • 对ddbs写入过于乐观,开始写入的时候启动了256个进程,单分片一秒写入13W行数据,导致主从延迟,影响到了一个非核心服务,于是将进程数改为了64个
  • 但整体效果还是很不错的,积累了迁移海量数据的经验,数据导入由于受到内部环境和业务的影响,方法不是最优的,但是也保证了我们不影响其他服务的同时,以最快的时间将数据迁移完毕
  • 跑数据一定不要忘记Screen !!!

发表评论

电子邮件地址不会被公开。 必填项已用*标注