背景程序员
在星爷的《大话西游》中有一句很是出名的台词:“曾经有一份真挚的感情摆在个人面前我没有珍惜,等我失去的时候才追悔莫及,人间最痛苦的事莫过于此,若是上天能给我一次再来一次的机会,我会对哪一个女孩说三个字:我爱你,若是非要在这份爱上加一个期限,我但愿是一万年!”在咱们开发人员的眼中,这个感情就和咱们数据库中的数据同样,咱们多但愿他一万年都不改变,可是每每事与愿违,随着公司的不断发展,业务的不断变动,咱们对数据的要求也在不断的变化,大概有下面的几种状况:面试
分库分表:业务发展愈来愈快,致使单机数据库承受的压力愈来愈大,数据量也愈来愈多,这个时候一般会使用分库的方法去解决这个问题,将数据库的流量均分到不一样的机器上。从单机数据库到分库这个过程,咱们就须要完整的迁移咱们的数据,咱们才能成功的分库的方式上使用咱们的数据。
更换存储介质:上面介绍的分库,通常来讲咱们迁移完以后,存储介质依然是一样的,好比说以前使用的是单机Mysql,分库以后就变成了多台机器的Mysql,咱们的数据库表的字段都没有发生变化,迁移来讲相对比较简单。有时候咱们分库分表并不能解决全部的问题,若是咱们须要不少复杂的查询,这个时候使用Mysql可能就不是一个靠谱的方案,那么咱们就须要替换查询的存储介质,好比使用elasticsearch,这种的迁移就会稍微要复杂一些,涉及到不一样存储介质的数据转换。
切换新系统:通常公司在高速发展中,必定会出现不少为了速度快而后重复建设的项目,当公司再必定时间段的时候,每每这部分项目会被合并,变成一个平台或者中台,好比咱们一些会员系统,电商系统等等。这个时候每每就会面临一个问题,将老的系统中的数据须要迁移到新的系统中,这个时候就更加复杂了,有可能不只是存储介质有变更,有可能项目语言也不一样,从更上层的角度来看,部门有可能也不一样,因此这种数据迁移的难度是比较高,风险也更加的大。
在实际业务开发中,咱们会根据不一样的状况来作出不一样的迁移方案,接下来咱们来讨论一下到底应该怎么迁移数据。redis
数据迁移算法
数据迁移其实不是一蹴而就的,每一次数据迁移都须要一段漫长的时间,有多是一周,有多是几个月,一般来讲咱们迁移数据的过程基本都和下图差很少:sql
首先咱们须要将咱们数据库已经存在的数据进行批量的迁移,而后须要处理新增的这部分数据,须要实时的把这部分数据在写完本来的数据库以后而后写到咱们的新的存储,在这一过程当中咱们须要不断的进行数据校验。当咱们校验基本问题不大的时候,而后进行切流操做,直到彻底切流以后,咱们就能够不用再进行数据校验和增量数据迁移。数据库
存量数据迁移设计模式
首先咱们来讲一下存量数据迁移应该怎么作,存量数据迁移在开源社区中搜索了一圈发现没有太好用的工具,目前来讲阿里云的DTS提供了存量数据迁移,DTS支持同构和异构不一样数据源之间的迁移,基本支持业界常见的数据库好比Mysql,Orcale,SQL Server等等。DTS比较适合咱们以前说的前两个场景,一个是分库的场景,若是使用的是阿里云的DRDS那么就能够直接将数据经过DTS迁移到DRDS,另一个是数据异构的场景,不管是Redis仍是ES,DTS都支持直接进行迁移。bash
那么DTS的存量迁移怎么作的呢?其实比较简单大概就是下面几个步骤:服务器
当存量迁移任务启动的时候,咱们获取当前须要迁移的最大的id和最小id
设置一个分段,好比1万,从最小id开始每次查询1万的数据给DTS服务器,交给DTS处理。sql以下:网络
`select * from table_name where id > curId and id < curId + 10000;` * 1
3.当id大于maxId以后,存量数据迁移任务结束
固然咱们在实际的迁移过程当中可能不会去使用阿里云,或者说在咱们的第三个场景下,咱们的数据库字段之间须要作不少转换,DTS不支持,那么咱们就能够模仿DTS的作法,经过分段批量读取数据的方式来迁移数据,这里须要注意的是咱们批量迁移数据的时候须要控制分段的大小,以及频率,防止影响咱们线上的正常运行。
增量数据迁移
存量数据的迁移方案比较有限,可是增量的数据迁移方法就是百花齐放了,通常来讲咱们有下面的几种方法:
DTS: 阿里云的DTS算是一条龙服务了,在提供存量数据迁移的同时也提供了增量数据迁移,只不过须要按量收费。
服务双写:比较适合于系统没有切换的迁移,也就是只换了存储可是系统仍是同一个,好比说分库分表,redis数据同步等,这个的作法比较简单直接在代码里面同步的去写入须要迁移的数据,可是因为不是同一个数据库就不能保证事务,有可能致使迁移数据的时候会出现数据丢失,这个过程经过后续的数据校验会进行解决。
MQ异步写入:这个能够适用于全部的场景,当有数据修改的时候发送一个MQ消息,消费者收到这个消息以后再进行数据更新。这个和上面的双写有点相似,可是他把数据库的操做变成了MQ异步了出问题的几率就会小不少
监听binlog: 咱们可使用以前说过的canal或者其余的一些开源的如databus去进行binlog监听,监听binlog的方式 就和上面的消息MQ方式同样,只是发送消息的这一步被咱们省略了。这个方式的一个开发量来讲基本是最小的。
这么多种方式咱们应该使用哪一种呢?我我的来讲是比较推荐监听binlog的作法的,监听binlog减小开发成本,咱们只须要实现consumer逻辑便可,数据能保证一致性,由于是监听的binlog这里不须要担忧以前双写的时候不是一个事务的问题。
数据校验
前面所说的全部方案,虽然有不少是成熟的云服务(dts)或者中间件(canal),可是他们都有可能出现一些数据丢失,出现数据丢失的状况总体来讲仍是比较少,可是很是难排查,有多是dts或者canal不当心抖了一下,又或者是接收数据的时候不当心致使的丢失。既然咱们没有办法避免咱们的数据在迁移的过程当中丢失,那么咱们应该经过其余手段来进行校订。
一般来讲咱们迁移数据的时候都会有数据校验这一个步骤,可是在不一样团队可能会选取不一样的数据校验方案:
以前在美团的时候,咱们会作一个双读,也就是咱们全部的读取都会重新的里面读取一份,可是返回的仍是老的,这个时候咱们须要作这部分数据的校验,若是有问题能够发出报警人工修复或者自动修复。经过这种方式,咱们经常使用的数据就能很快的进行一个修复,固然也会不定时的去跑一个全量的数据check,只是这种check出来修复数据的时间就比较滞后。
如今在猿辅导以后,咱们没有采用以前的那种方式,由于双读check虽然能很快发现数据的不对,可是咱们并无对这部分数据有那么高的一个实时性校验而且双读的一个代码开发量仍是稍微比较大的,可是又不能依靠不定时全量check去保证,这样就会致使咱们的数据校验时间会很是的延长。咱们采起了一个折中的方法,咱们借鉴了对帐里面的T+1的一个思路,咱们天天凌晨获取老数据库中昨天更新的数据,而后和咱们新数据库中的数据作一一比对,若是有数据不同或者数据缺失,咱们均可以立马进行一个修复。
固然在实际开发过程当中咱们也须要注意下面几点:
数据校验任务的一个正确性如何保证,校验任务原本就是去校订其余数据的,可是若是他自身出现了问题,就失去了校验的意义,这里目前来讲只能靠review代码这种方式去保证校验任务的正确性。
校验任务的时候须要注意日志的打印,有时候出现问题多是直接全部数据出现问题,那么校验任务就有可能会打出大量的错误日志,而后进行报警,有可能会将系统打挂,或者说影响其余人的服务。这里若是要简单一点搞,能够将一些非人工处理的报警搞成warn,复杂一点搞得话,能够封装一个工具,某个error打印再某个时间段超过必定量而后就不用再打印了。
校验任务注意不要影响线上运行的服务,一般校验任务会写不少批查询的语句,会出现批量扫表的状况,若是代码没有写好很容易致使数据库挂掉。
切流
当咱们数据校验基本没有报错了以后,说明咱们的迁移程序是比较稳定的了,那么咱们就能够直接使用咱们新的数据了吗?固然是不能够的,若是咱们一把切换了,顺利的话固然是很好的,若是出现问题了,那么就会影响全部的用户。
因此咱们接下来就须要进行灰度,也就是切流。对于不一样的业务切流的的维度会不同,对于用户维度的切流,咱们一般会以userId的取模的方式去进行切流,对于租户或者商家维度的业务,就须要按照租户id取模的方式去切流。这个切流须要制定好一个切流计划,在什么时间段,放出多少的流量,而且切流的时候必定要选择流量比较少的时候进行切流,每一次切流都须要对日志作详细的观察,出现问题尽早修复,流量的一个放出过程是一个由慢到快的过程,好比最开始是以1%的量去不断叠加的,到后面的时候咱们直接以10%,20%的量去快速放量。由于若是出现问题的话每每在小流量的时候就会发现,若是小流量没有问题那么后续就能够快速放量。
注意主键ID
在迁移数据的过程当中特别要注意的是主键ID,在上面双写的方案中也提到过主键ID须要双写的时候手动的去指定,防止ID生成顺序错误。
若是咱们是由于分库分表而进行迁移,就须要考虑咱们之后的主键Id就不能是自增id,须要使用分布式id,这里比较推荐的是美团开源的leaf,他支持两种模式一种是雪花算法趋势递增,可是全部的id都是Long型,适合于一些支持Long为id的应用。还有一种是号段模式,这种会根据你设置的一个基础id,从这个上面不断的增长。而且基本都走的是内存生成,性能也是很是的快。
固然咱们还有种状况是咱们须要迁移系统,以前系统的主键id在新系统中已经有了,那么咱们的id就须要作一些映射。若是咱们在迁移系统的时候已经知道将来大概有哪些系统会迁移进来,咱们就能够采用预留的方式,好比A系统如今的数据是1到1亿,B系统的数据也是1到1亿,咱们如今须要将A,B两个系统合并成新系统,那么咱们能够稍微预估一些Buffer,好比给A系统留1到1.5亿,这样A就不须要进行映射,B系统是1.5亿到3亿,那么咱们转换成老系统Id的时候就须要减去1.5亿,最后咱们新系统的新的Id就从3亿开始递增。
可是若是系统中没有作规划的预留段怎么办呢?能够经过下面两种方式:
须要新增一个表,将老系统的id和新系统的id作一个映射记录,这个工做量仍是比较大的,由于咱们通常迁移都会涉及几十上百张表,记录的成本仍是很是的高。
若是id是Long型的话,咱们能够好好利用long是64位这个因素,咱们能够制定一个规则,咱们新系统的id都是从一个比较大的数开始,好比从大于Int的数开始,将小Int的那部分数均可以留给咱们的老系统作Id迁移,好比咱们上面的1.5亿的数据量,其实只用了28位,咱们的Int是32位,那么还有4位可使用,这个4位能够表明16个系统作迁移,固然若是规划中有更多的系统作迁移,能够将新系统的id起始点设置得更大一点。以下图所示:
总结
最后简单来总结下这个套路,其实就是四个步骤,一个注意:存量,增量,校验,切流,最后再注意一下id。无论是多大量级的数据,基本上按照这个套路来迁移就不会出现大的问题。但愿能在你们的后续迁移数据工做中,这篇文章能帮助到你。
如下文章来源于咖啡拿铁 ,做者咖啡拿铁
连接:https://mp.weixin.qq.com/s/H3...
总结了一些2020年的面试题,这份面试题的包含的模块分为19个模块,分别是: Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM 。
获取资料以上资料:关注公众号:有故事的程序员,获取学习资料。记得点个关注+评论哦~