数据迁移经验总结——亿级别多表异构的数据迁移工作

由于系统改版,最近三个月在做数据迁移工作,由于业务的特殊,基本将数据迁移所能踩的坑都踩了一遍,决定好好做个总结。

迁移类型——新老系统表结构变化较大的历史数据

一、核心问题

1.新老表结构变化极大。新表是以deliver为核心,另外还涉及仓储系统的一张表,订单系统的4张表,并按照新的逻辑映射关系进行迁移。

2.增量数据迁移。在全量数据迁移时必然会有新的数据,这些数据应该实时进行迁移

3.亿级别数据性能、效率的考虑。由于订单业务非常重要,数据迁移带来的qps对数据库的压力非常大,需要不断测试迭代找到一个适合的迁移效率和性能。

4.老数据格式问题。12、13年的数据由于历史悠久,会存在缺数据,数据不全的问题,这些问题都应该在程序中进行容错。

5.核对数据比较困难。由于新表涉及多个旧表且数据量庞大,数据核对比较困难,目前自己写脚本按周并根据一定的逻辑进行数据核对,同时辅以肉眼比对。

二、数据迁移流程

全量同步 java程序

增量同步 otter + 扩展的java程序

历史数据 需同步到 后端backend 和 前端cobar库

1.历史数据按年份下载成多个文本(1个文本2g左右)

2.多线程读文本数据

3.处理类型主要有insert batch_insert update ,根据类型不同,线程调用不同逻辑方法处理数据并写入新表

三、展开讨论

将上面问题进行展开,并总结其中的技术问题

1.项目架构层次

该迁移项目采用java开发,仅仅作为迁移,定位轻量级,后端db采用jdbcTemplate , spring管理javabean 分为三层, 一层manager主要对数据流根据类型分发给不同业务逻辑处理 一层业务逻辑——处理映射关系 一层dao。

2.db相关问题

后台db 是一主多从 主用来接受写请求,从都是读请求。 从库性能远远低于主库

2.1 调用接口取数据的问题

刚开始做时,由于想把项目做的很轻,获取老数据采用调接口的形式,调用order接口、wms接口获取就可以获取相应数据。但接口是很脆弱的,经统计http接口每秒只能抗500-1000左右的请求,大数据量下接口很快就成为瓶颈。我测试过一次,1亿数据需要2周时间才能导完,所以必须放弃调用接口,自己读db进行查询。

2.2 冷热库问题

历史数据大部分在冷库里,在db查询是需要先查热库再查冷库,并按照相应的逻辑进行查库。

2.3 batch批量插入 主从延迟问题

一开始数据处理一条插入一条,这样1亿数据要产生1亿条insert语句。对于主库来说写入是没有影响的,但是从库有很大问题,其性能和主库相差深远,同时在同步binlog上会产生很大的延时,一亿insert意味着一亿次commited 一亿次提交 binlog同步时就会进行1亿次网络传输,从库也会执行1亿次insert。这时候延迟还会产生更大的问题,因为主库还有其他业务数据写入,从库没有实时同步,导致客服查不到订单。

后来改成多value的insert语句,每次处理一批数据,一个commit提交。这样即提高了insert插入性能,又解决了延迟问题。

jdbcTemplate改为updateBacth()方法,但发现sql语句还是一个数据一个insert,这是必须让jdbc如何支持batch insert, 需要在jdbc连接加上参数 jdbc:mysql://.....&rewriteBatchedStatements=true ;

2.4  sql的dead connection

多线程读数据时,发现有几个线程一直阻塞了,jstack后,发现Mysql报sokcetAvaiable,这个错误一般发生在连接前端cobar时。这是因为jdbc连接没有设置连接超时,要加上connectTime socketTIme,这个是自己应用连接数据库的网络超时时间,如果不设置就会一直等待下去。

设置参数:&failOverReadOnly=false&connectTimeout=60000&socketTimeout=60000 failOverReadOnly jdbc默认超时重试就变为只读了,必须加上failOverReadOnly参数

3. 程序级优化

3.1 全量数据的多线程解决方案

单进程迁移,1亿数据大概需要1周时间,时间和效率都太低了,所以思考改为多线程。其实改多线程很简单,历史数据是文本形式,只需要多线程去逐一读文本,并发处理就可以了,同时采用countdownlatch保证线程全部完成后在退出主程序。

3.2 增量数据的多线程解决方案

otter会顺序的消费binlog,消费过程是很快的,但如果采用单进程模式,瓶颈会卡在我的写入程序上,所以将程序该为多线程。otter每次读取2000个binlog记录,主进程一旦获得满2000个后,再启动多线程去并发处理这些记录。还需要注意问题,有可能同一时间段针对一条记录有多个改动,需要以最后一次改动为准,所以我将线程个数最为桶的个数,按照order_id和桶的余数放入到对应的桶里,同时通的value是一个map (order_id , row对象),这样可以保证每一批处理都是最新的记录。

3.3 otter的扩展

以后我会再写一篇博文分析otter,这里先简要介绍一下。otter是阿里的一个开源项目,主要解决异地机房实时数据备份的问题,它的原理就是通过把自己伪装成slave去读取master的binlog然后解析成eventData对象并写入新的表里。它默认情况下仅支持一对一的复制、表的字段映射逻辑非常简单的复制。但阿里的项目就是强大,它给用户提供了一个接口,继承之后就可以按照自己的逻辑去同步数据了。继承类是AbstractEventProcessor,这个类会接受到获取的eventData对象(就是binlog一条内容),这里面有你订阅的表的所有字段值,sql类型等。继承后,在该类的方法里引入自己的同步程序,这样otter订阅的数据就会进入自己的程序了。

4.数据补救策略

数据迁移后依然会存在一些问题:

1.增量过程中,由于网络等原因会造成binlog读取的卡顿,canal会进行重试,但是貌似该时间点上的数据依然会丢失

2.迁移过程中,有一些时间点的误差导致数据丢失或异常。

由于新旧表结果相差很大,数据量也太大了,不能简单用一条sql,这样会拖库。所以我写程序一周一周核对数据,主要核对新表有旧表没有的,这些需要删除;旧表有新表没有的,这些需要添加;新表旧表status不一致的这些需要根据旧表更新。

小结:

1.性能——在迁移前对mysql性能,数据量大小都没有一定的认识,采用接口获取数据后改用读库多线程,一前一后浪费了半个多月的时间,再做迁移一定要考虑好数据量,迁移时间,表结果,mysql性能。

2.代码编写——该程序耦合太严重,后期每增加一种操作类型,原代码上都要加很多判断,下次再写必须要面向接口编程,使用继承和工厂方法(参考秒杀项目);jdbcTample很轻量级,单sql多了后,代码量还是会上来,下次提前做好评估,用mybatis;校验脚本和执行脚本过多,日志打印也过多,下次还是采用log4j可输出多文本,同时脚本书写上要更优雅和简洁。

3.核对数据——数据迁移是一件脏活累活,这次迁移有一大半时间在进行数据和对,不停写sql,不停看校验数据。以后再有类似的迁移必须要先规划好,怎样进行核对,例如选择几个关键字段做hash。

参考文献:

1.《关于数据迁移的方法、步骤和心得》http://m.blog.csdn.net/blog/baoqiangwang_11109/5492910

2.  Jdbc驱动的rewriteBatchedStatements参数 http://www.cnblogs.com/chenjianjx/archive/2012/08/14/2637914.html

3.  深入理解JDBC的超时设置 http://www.importnew.com/2466.html

4. otter https://github.com/alibaba/otter

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-31 21:16:54

数据迁移经验总结——亿级别多表异构的数据迁移工作的相关文章

ASM时的OFM特性对影的建数据文件名的影响及为SYSTEM表空间的数据文件使用别名

客户遇到个DG的问题,存储使用的ASM管理,有多个磁盘盘. 在主库创建数据文件,备库自动创建的数据文件都在同一磁盘组,并且在主库创建数据文件是指定的是类似**.DBF的名字,到备库也变成了使用ASM的数字格式**.266.123456 这是因为使用了OMF特性. OMF,全称是Oracle_Managed Files,即Oracle文件管理. 使用OMF可以简化管理员的管理工作,不用指定文件的名字.大小.路径,其名字,大小,路径由oracle 自动分配.在删除不再使用的日志.数据.控制文件时,O

数据迁移之sql server2005 中两表关联更新数据操作

近期在做数据迁移的工作,老系统的数据迁移到新系统,当中麻烦的确不少,因为是重新设计的系统与老系统中有太多的不一至性,例如表结构,字段的处理,像附件的存储方式,还有历史遗留数据... 后面会慢慢的把相关技术处理细节展现出来,当然,难度不大,只是琐碎事情太多. 今天就简单来看看数据的关联更新. 业务场景: 同一个业务,有一张主表及一张子表,字段差不多,只是老系统中主键生成方式GUID,而新的主键生成方式自增长型,这里就不论合不合理了,来看看我们的问题. 我们要做的就是把老数据导入到新表,并且保持他们

DropDownList 绑定数据后 插入一条不属于表中的数据

ddlFGiftId.DataSource = dtGift; ddlFGiftId.DataTextField = "FGiftName"; ddlFGiftId.DataValueField = "FGiftName"; ddlFGiftId.DataBind(); ddlFGiftId.Items.Insert(0, new ListItem("==不使用赠送==", "0")); ddlFGiftId.Selected

在线数据迁移经验:如何为正在飞行的飞机更换引擎

在线数据迁移,是指将正在提供线上服务的数据,从一个地方迁移到另一个地方,整个迁移过程中要求不停机,服务不受影响.根据数据所处层次,可以分为cache迁移和存储迁移:根据数据迁移前后的变化,又可以分为平移和转移. 平移是指迁移前后数据组织形式不变,比如Mysql从1个实例扩展为4个实例,Redis从4个端口扩展到16个端口,HBase从20台机器扩展到 30台机器等等.如果在最初的设计里就为以后的扩容缩容提供了方便,那么数据迁移工作就会简单很多,比如Mysql已经做了分库分表,扩展实例的时候,只需

【DataMagic】如何在万亿级别规模的数据量上使用Spark

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由鹅厂新鲜事儿发表于云+社区专栏 作者:张国鹏 | 腾讯 运营开发工程师 一.前言 Spark作为大数据计算引擎,凭借其快速.稳定.简易等特点,快速的占领了大数据计算的领域.本文主要为作者在搭建使用计算平台的过程中,对于Spark的理解,希望能给读者一些学习的思路.文章内容为介绍Spark在DataMagic平台扮演的角色.如何快速掌握Spark以及DataMagic平台是如何使用好Spark的. 二.Spark在DataMagic

es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?

面试题es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?面试官心理分析这个问题是肯定要问的,说白了,就是看你有没有实际干过 es,因为啥?其实 es 性能并没有你想象中那么好的.很多时候数据量大了,特别是有几亿条数据的时候,可能你会懵逼的发现,跑个搜索怎么一下 5~10s,坑爹了.第一次搜索的时候,是 5~10s,后面反而就快了,可能就几百毫秒.你就很懵,每个用户第一次访问都会比较慢,比较卡么?所以你要是没玩儿过 es,或者就是自己玩玩儿 demo,被问到这个问题容易懵逼,显示出你对

【oracle11g,14】表空间管理3:临时表空间,表空间的脱机和只读,数据文件迁移,更改表空间数据文件的大小,表空间数据文件的迁移,使用非标准块的表空间,bigfile 表空间

一.临时表空间: 如果临时表空间不足会报ora-1652错误. 二.什么时候使用临时表空间: 排序和分组 索引create或rebuild order by 或group by distinct 操作 union或intersect或minus sort-merge joins analyze 用于排序.分组.索引等操作,在pga中的sort_area中排序,会将排序的中间结果存放到临时表空间中,如果想提高排序的效率可以提高sort_area_size参数值. 临时表空间不能存放持久化对象,推荐

mysql5.7 InnoDB数据表空间文件平滑迁移

[ERROR] [FATAL] InnoDB: Tablespace id is 14 in the data dictionary but in file ./mysql/innodb_index_stats.ibd it is 696! --先测试直接把.ibd文件拷贝过去   (这种方法失败,因为tablespace_id不一致) mysqld_safe --defaults-file=/usr/my-new.cnf &    --启库 总结: 对于innodb每个表都有各自的表空间来说,

Elasticsearch+Mongo亿级别数据导入及查询实践

数据方案: 在Elasticsearch中通过code及time字段查询对应doc的mongo_id字段获得mongodb中的主键_id 通过获得id再进入mongodb进行查询   1,数据情况: 全部为股票及指数的分钟K线数据(股票代码区分度较高) Elasticsearch及mongodb都未分片且未优化参数配置 mongodb数据量: Elasticsearch数据量: 2,将数据从mongo源库导入Elasticsearch import time from pymongo impor