粉丝1.3万获赞4.7万

兄弟们,今天跟大家聊一个美团场景题啊,如何在生产环境不停服的情况下进行数据的迁移,从原来的十六张表迁移到六十四张表中。 首先,这是一个典型的数据库扩容问题啊,从十六张表扩容到六十四张,核心难点在于怎么保证不停服,而且数据不能丢,不能乱。我的思路就是双写加数据迁移加会度切换。这套组合拳具体怎么落地呢?我会分为四个阶段来搞啊。第一个阶段是双写,准备首先把六十四张新表建好,表结构跟旧表保持一致啊。 然后啊,重点是改造代码层的路由逻辑。这里啊,我会在 d o 层加一个开关,控制双写行为。写操作的时候啊,同时往旧的十六张表和新的六四张表里面都写一份。这个过程中啊,如果新表写失败了,我只记录日期,不影响主流程啊,因为这个阶段还是以旧表为准读,操作暂时还是全部从旧表中读啊, 线上的业务完全不受影响。第二个阶段是历史数据迁移啊,这块是一个细活,我会写一个独立的迁移程序,在凌晨的业务低峰期跑啊。迁移策略,用主键分段,比如每次迁移十万条数据,然后 sleep 一下,避免对数据库造成太大压力。 这里有个关键点啊,就是路由规则它变了,原来十六张表用的是哈希 uziid 除以十六来分片的,现在六十四张表就用哈希 uziid 除以六十四。所以 你迁移的时候得重新计算每条数据应该落到哪张新表里啊。迁移的过程中啊,因为双写还在进行,所以新表可能已经有部分数据了,这个时候要做好密等处理,用 inside on duplicate key update, 或者先查询再决定插入还是更新啊。迁移完一批数据后,我还会做数据的校验,写一个对比脚本,随机抽取一些数据,比对新旧表的记录数和关键时段的 check sum, 确保数据的一致性啊。 三个阶段是切换读流量,这个是最关键也是风险最高的环节,所以一定要进行灰度处理啊。我会先在配置中心加一个灰度开关,控制读流量的比例啊,一开始只切百分之五的,读 紧就到新表,然后密切观察监控指标,看接口的耗时错误率,数据库连接数这些有没有异常啊。如果稳定运行个半个小时没有问题,就继续提升比例到百分之二十,百分之五十啊,最后到百分之百。这个过程中,双写一定要一直保持着,因为万一新表出了问题,可以立即把读流量切回旧表 数据,不会有任何的损失啊。切换期间,如果发现新表的数据有问题,可以暂停切换,修复数据后再继续啊。最后第四个阶段就是清理收尾啊,再次进行全量比对交易,并且等新表稳定后运行了。比如一周时间各种监控指标都正常,业务方也没有反馈问题,这个时候就可以停掉双写了。代码里面只是 是把双写逻辑去掉,只写一个新表,然后再观察个几天,确定完全没有问题了,就可以把旧的十六张表下线了,释放资源。整个过程我会做好每个阶段的监控和告警啊,比如双写成功率、迁移进度、新旧表数据差异这些关键指标,出问题了第一时间发现并且进行回滚啊。 我觉得这个方案最大的优势,尽量支持随时可回滚,每个阶段都是渐近式的封写可控,应该能满足生产环境。不停。兄弟们,我们直播间见。

歪哥你好,在网上刷到你那个不停服务进行数据库迁移的视频,想跟你交流一下。这是 一个典型的数据库扩容问题啊,从十六张表扩容到六十四张,核心难点在于怎么保证不停服,而且数据不能丢,不能乱。我的思路就是双写加数据迁移加会度切换,这套组合拳具体怎么落地呢?我会分为四个阶段来搞啊。第一个阶段是双写,准备首先把六十四张新表建好,表结构跟旧表保持一致, 然后啊,重点是改造代码层的路由逻辑。这里啊,我会在 d o 层加一个开关,控制双写行为,写操作的时候啊,同时往旧的十六张表和新的六四张表里面都写一份。关于双写阶段,我有几个问题,第一, 是否有什么现成的方案可以简化双写的代码改造还是需要硬编码双写逻辑。第二,双写的逻辑过于复杂,我担心能不能设计的相对完善。我举个例子吧, 比方说这有一个更新某个字段的接口,在多现成的情况下,有两个请求同时访问这个接口,请求一把字段更新成 a, 请求二把字段更新成 b。 我们假设请求一先执行完这个更新语句,请求二后执行这个更新语句,那数据的最终状态就是以请求二为准。请求一的更新其实是被覆盖了,也就是说数据的最终状态这个字段应该是 b, 但是写辛苦的那个语句,它能保证是请求一先执行完成吗? 应该很难保证吧,不能保证的话,新酷就酷的数据就不一致了。这个问题要怎么解决?我再举个例子,这是一个最简单的 insert 的 语句,双写的代码是这样写吗?这样写肯定是有问题的, 写旧库的时候会生成一个 id, 写新库的时候也会生成一个 id, 那 这两个 id 就 不一致了。如果想要 id 一 致,插入新库的时候,就要使用旧库里那个 id, 也就是写新库之前需要设置一下 id, 同时需要把写新库的 s q l 改一下,需要手工设置 id, 虽然代码不太优雅,逻辑没有问题。我们再看看切流量的开关代码怎么写。流量开关状态有三个状态,一,只写旧库 状态。二,同时写旧库和写新库状态。三,只写新库,不写旧库。写完了流量开关代码是这样的,双写这个分支跟只写新库这个分支里的定色的语句是不一样的。双写时 id 是 指定的,只写新库时 id 是 自增的。代码越来越乱了, 这只是伪代码,还有事务代码、异常逻辑等等,这里就不继续展开了。代码越写问题越多。除了我举的这几个例子,还有没有其他需要注意的点?这些问题能事前评估完整吗?我要打个问号。第三, 双写阶段如何处理?更新 s q l 跟删除 s q l? 假设双写在这个时间点开启,此时新库是空的,没有数据,接着有一个更新 s q l 的 请求, 在旧库更新完数据之后,写入新库的时候,会因为新库里这个 id 的 数据不存在,导致更新失败,或者说更新行数为零, 这里会导致大量的双写失败。这种更新失败需要怎么处理?有没有什么性能可控,工作量也可控的优化方案。 第二个阶段是历史数据迁移啊,这块是一个细活,我会写一个独立的迁移程序,在凌晨的业务低峰期跑啊。迁移策略用主键分段,比如每次迁移十万条数据, 然后 sleep 一下,避免对数据库造成太大压力啊。迁移完一批数据后,我还会做数据的校验,写一个对比脚本,随机抽取一些数据,比对新旧表的记录数和关键时段的 check sum, 确保数据的一致性啊。关于全量数据导入,我问一个问题, 这里讲到的是每十万条迁移一次读取十万条数据是需要时间的,后续的写入十万条数据也是需要时间的。我们以这十万数据的最后一条数据为例,它有可能在新库里面是不存在的。假设这条数据在新库里不存在,它读取之后直到它写入完成, 这中间会有一定的时间间隔,这个时间取决于前面九万九千多条的数据写入需要多久。我们假设需要十秒钟,如果在这十秒钟之内,这条数据再就库又被变更了, 那我即将写入新库的这个 s、 q、 l 是 不是就脏了?因为这条数据在新库里面不存在,双写阶段会失败,最终写入新库的数据就是脏数据了。每批迁移的数据少一点,出现脏数据的概率会变小, 就算业务高峰期也会有数据的更改,不能完全避免整个全量迁移过程。如何保证数据的一致性?还是说留到对比阶段再说说。对比。 新库也好,旧库也好,两个集合每时每刻都在变化,新库对于旧库来说还有一定的数据延时,如何进行对比? 对于数据库里面最后更新的那些数据行在做对比的时候,因为去新库取一次数据和去旧库取数据本身就不可能同时进行,再加上两个数据库有延时,很可能对比出来数据是不一致的,但其实没问题, 如果不能排除这些噪音,我们怎么才能精确的定位到真正的不一致的数据?再说说切流量,提个问题,我现在就库插入一条数据,然后去新库查询, 新库因为有双写的延时,有可能查不到数据,这种要怎么处理?最后再说说工作量跟风险的问题,首先这个迁移方案 我们到底需要考虑多少种场景,包括普通场景、极端场景以及异常场景,如何做到不遗漏视频的?前面举了一些简单的问题,场景,更多的场景以我的能力没有办法完全猎取出来。除了方案的复杂,场景的复杂,开发的工作量如何? c r u d, 每个数据操作的地方都要进行修改,各种切换开关。还有就是我们的测试场景需要怎么设计?这种非功能性的功能需要如何设计?测试用力用力,设计的难度如何?回归的工作量如何? 这个方案真的可行吗?可以保证最后能顺利地迁移吗?最后问一个问题,为什么大厂不使用双写的方案?

好,同学们,我们来看一下来自美团的一个面试题,在 get 位中怎样实现服务的平滑迁移啊?它适用的薪资范围呢?是三十到三十五 k, 岗位呢是高级开发工程师。 那我们先理解在 get 位中怎样实现服务平滑迁移的它的一个场景。 好,来我们看一下,在这呢,一般我们发送请求,他都会通过哎,前端的 get 位网关, 对不对?好,通过这网关之后,然后他会进行相应路由后面的服务中,比方说我们到马视频杠 o 的 这个订单上, 对吧?啊,我们,哎,它一路由路由到这里,这个路由到这里呢?它这里面呢有许多功能啊,比方它是有三台服务器吧, 对吧?它这里面有许多功能,我们给它新增加一个功能啊,比方在原来基础上更改了一个功能,比方这里面把视频杠 alt 给它增加了新的一个功能,我们给 啊应该更改原来的功能吧,更改原来功能更好,对吧?此时呢,我们想的,哎,这不能一下的把所有流量都打到这个新的上面去,万一我们的更改的代码有什么问题呢? 怎么办?是吧?我们想怎么样,原来一些请求呢?还是打到老服务上,老服务上占百分之八十的流量, 百分之八十流量在老服务上,咦的百分之二十的流量到这个上面去, 然后如果发现百分之二十流量没什么问题的话,我们接着呢就把哎一点点的百分之在这百分之四十,百分之六十,百分之一百完全的迁移过来,它是这样一个过程。那为解决这个问题呢,我们应该怎么做呢? 大家可以暂停下来思考一下。好,来我们看一下这里,在这里呢,我们 get 位中呢,它是中有一个的 vt 这样路由的 vt 这个路由的一个断言工厂,它可以对每个服务呢进行权重的配置, 比方有百分之六十路由到这里啊,百分之四十的路由另一个服务啊,它可以进行这样配置,而它有这个功能,但是我们更改的时候,我不能,不能,哎呀更改的配置了,然后再重启服务吧,所以说呢,我们把这些配置呢都放到 necos 里面中, nico 配置中心,是不是我们实时更改,实时发生变化,这样呢整体的是什么?使用 vt 路由的断言工厂进行服务的权重配置,并将这个配置呢放到我们 nico 中配置中心中 啊,然后呢进行动态的迁移,因为你可以动态更改嘛。好,我们再看一下这 vt vt 的 路由的断言工厂,它的一个配置其实就是这么个东西啊, 这里呢 group 组,它可以呢,这个组呢百分之八十八至二,这所有的你通过这个路由过来以后啊,它的请求,它的请求之后来到这是百分之八十,来到这是百分之二十, 这就是这么一个过程啊,大家可以看一下。好。

如何不停机进行数据迁移?哈喽,大家好,我是鼓炮科技联合创始麦克,今天给大家分享一下这个面试题的回答思路。另外想要文字版本回答的小伙伴,可以在我的评论区置顶中去领取三十万字的面试文档。 这个文档不仅包含了各个一线大厂的常见面试真题,而且对每道题目都进行了详细的解答,能够帮助你们很好的提高面试的通过率。 很明显,这是一个场景问题,如果求职者呢,有数据迁移的经验,很容易就能够回答出来,而且还能结合自己在实践共中遇到的问题和解决方案进行综合描述,更好的突出个人的优势。 这个问题对求职者如何分析问题,如何制定解决方案,是否能够考虑到可能的问题,并提前规划对应的措施等方面做一个综合评估。另外,不停机数据迁移 涉及到系统架构、数据库迁移、数据同步等多个方面,需要求职者有扎实的系统规划和设计能力。下面我们来看一下这个问题的完整回答。 对于这个问题,首先要做的就是把旧库的数据复制到新库中,而由于旧库还在服务线上的业务, 所以不断会有数据写入到旧库,我们不仅要往新库复制数据,还要保证新旧两个库的数据实时同步,所以我们需要一个同步程序来实现新旧两个数据库实时同步。这里我们可以采用双写的方案,在进行数据迁移的同时, 旧系统和新系统都同时写入数据,这种方案在数据签印完成之前,需要保持两套系统并行运行,有一定的系统资源开销,但是可以实现平滑过渡。当新系统的数据和旧系统数据保持一致,新系统稳定 运行以后,就可以逐渐将流量切换到新系统,并逐步停用旧的系统。不过啊,切换到双显之后,新库和旧库的数据可能会存在不一致的情况,原因有两个, 第一,停止同步程序或者开启双写,这两个过程很难做到无缝衔接。第二,双写的策略也不能保证新库和数据的强硬之性。这个时候我们还需要一个对比和补偿的程序,这个程序对比旧库最近数据的变更,然后检查新库中的数据是否一致, 如果不一致就要进行补偿。开启双险以后呢,还需要稳定运行至少几周的时间,并且我们要不断的去检查,确保不能有旧库写入成功,新库写入失败的情况出现。对比程序也没有发现新旧两个库的数据有不一致的情况,这个时候我们就可以认为新旧两个库的数据 是一直保持同步的。接下来就可以用类似灰度的方式把读请求逐步切到新库上,如果出现问题的话,可以再切回到旧库。当全部读请求都切换到新库上之后,这个时候其实读写请求就已经都切换到新库了。 至此呢,我们就完成了在线更换数据库的全部流程。以上就是我的理解,我是麦克,感谢大家的点赞和关注,我们下期再见!

本次投稿面试中,被问到 threadlocal 父子现成之间如何做到共享传递数据,这道题属于 java 并发编程的高频面试题,看似是问怎么传数据,实则是多层级考察后人对 threadlocal 的 理解深度和并发编程的实战能力。 下面我们一起来看看具体解析。 threadlocal 它的主要作用是现成隔离,每个现成都会有自己的副本,主要的场景有数据库的连接管理啊,用户的绘画管理等等,这些场景我都需要。现成之间的数据是相互不影响的。 为了帮大家更系统性的准备面试,我整理了包括各阶段的面试题,加完面试图及学习路线,以及精选的简历模板,需要的小伙伴七八九双手奉上。那 sort of local 它是怎么去做到我们的数据不影响的呢?我们直接去看一下它的方法来,我去 set 的 时候,那么它有一个 sort of local 的 map, 那么这个 map 我 先会去从县城 thread 里面去拿,那么在我的 thread 里面有一个 thread 的 logo 字,好,它就是一个 thread 的 logo, 它里面去设置值的时候,那么它是怎么去设置?可以去看它是有一个宿组,一个 int 的 宿组,这个 int 它的 key 是 我们的一个 thread 的 log 对 象的未引用,然后白按钮是你 set 的 值。 好,接下来你看通过取模的方式拿到一个下标,当然它里面会有解决下标冲突的逻辑。好,接下来你看通过取模的方式拿到一个下标冲突的逻辑。好,接下来你看通过取模的方式,它的原理。 我去 set 的 时候,每一个 select 它都会有一个 select 的 那个 mac, 这个 mac 它是一个 nt 的 宿主,我去设置值的时候,基于这个 select 的 对象去取模,得到一个下标值,然后以 nt 的 对象去保存 nt t 是 我们的 select 对 象的弱引用按钮是我们 set 的 值好,那么这样子它就做到了,县城之间它是隔离的,因为每个县城它都有一个副本。 好,那么这时候我们来看,假如说我在主县城,也就是 main 县城里面,我去 set 一 好,我在主县城肯定是拿不到的,我们去跑一下, 你看主县城能拿到,但是子县城是 no。 那 么假如说我在子县城里面,我想拿到这个副县城的值,那怎么办呢?我们就要用到一个 in hero table 属性的 local 好, 我们把属性的 local 改成什么呢? in hero 属性的 local 好, 那么这一个是个什么?它其实也是继承于我们的属性的 local, 它属于属性的一个子类,那么它实现的功能其实很简单,它能拿到副县城设置的一个值,你看主县城能拿到,子县城也能拿到,那么这个时候很多人有疑问,他是怎么做到的?好,那么先我们来看一下 in hero table 属性的 local 这个 class 对 象它是怎么样子的? 我们可以看到它继承于 swiftlocal, 并且只重写了三个方法,一个是 char 的 value, 一个是 getmap, 一个是 createmap, 并且重写以后,它的逻解 其实很类似,你看 createmap, 它也是去创建一个 swiftlocalmap, 也是基于 swiftlocalmap 去保存我们的信息,只不过 它给到的变量是 swiftlocal 的 you had type swiftlocals, 刚刚的 swiftlocal 是 一个 swiftlocals。 好, 现在的 you had type swiftlocals, 它的变量是不一样的,它操作的副本是不一样的, 看他怎么去做到。我能够去获取到副县城的一个 value, 因为你看,虽然是有单独的一个变量,但是他还是放在我们的一个 street 里面的,那每个县城不都是独立的吗?还是独立的,那他怎么拿到副县城的 value 呢? 他的一个核心在哪里呢?在我去 new street 的 时候,去创建县城的时候,那我会有一个关于 you had table street locs 的 一个逻辑处理, 你看在这里面它默认是 two 好, two 以后,我会先拿到我的副现成,因为这个当前的现成是我去 new three 的 现成,好拿到副现成。然后如果我传过来的是 two, 并且副现成的 intitle three locos, 它如果不尾空的时候,那么我会去基于我们副现成的 intitle three locos 去创建一个 intitle three locos 卖, 并且给到我们的 in html 属性 locos, 所以 就相当于他去拿到负类的 in html 属性 locos, 基于他再去创建子县城自己的 in html 属性 locos, 所以 这个流程其实比较简单,你看我去创建县城的时候,我去判断 f 县城是否存在 in html 属性 locos, 如果存在,我拿到 f 县城的 in html 属性 locos, 然后再基于他去创建我们自己的 html 属性 locos, 那 么它的底层其实是一样的,都是基于我们的 html 属性 locos, 那 么它的底层其实是在县城初步化的时候, 那么才会去拿到我们赋现成的一个值,然后再创建自己的一个副本变量。那么假如说我们配合现成池使用的时候,因为现成池中现成是会赋用的,没有初步化的过程,那么就会出现可能跟你想要的结果是不一致的问题。

我去面试一个架构师岗位,面试官问我说百亿数据数据库如何不停浮的平滑迁移?答,互联网有很多数据量比较大、并发量比较大、业务复杂度比较高的业务场景,它们的典型系统分层架构如下,其上游是业务层 business, 中游是服务层 service, 下游是数据库层 db。 互联网架构很多时候都面临着这样的一些需求,第一, 底层表结构变更啊,数据量非常大的情况下,数据表增加一些属性,删除一些属性,修改一些属性。第二,分库个数的变化,由于数据量持续增加,底层分库个数可能需要增加。第三,底层存储戒指发生变化。比如说底层的存储引擎由一个数据库换成了另外一个数据库, 这样的种种需求都需要进行数据迁移,迁移的过程不停机,保证系统持续提供服务。最常见的有四种方案, 停符、离线迁移法、不停符、追日制法、不停符、双斜法、不停符、一零二讲中讲过的秒级扩容法四种方案万字介绍,详见评论区。 增加分库的个数。存储引擎的升级,大家一般是怎么实现的?咱们一起聊一聊。