作为一个以新闻、资讯为主的 App,今日头条上的主要内容都是由文章组成,文章服务自然伴随着今日头条 App 的产生就已出现,之后又逐步扩展为目前的内容云,为头条、西瓜、小说、懂车帝等多个 App 服务的业务内容中台。截止 2021 年底,内容云接入子业务已经达到数百个,高峰期主要读服务 QPS 数百万,维护超过 2200 个属性,存量数据达到百亿条级别。然而由于历史悠久,经手人众多,加上历史上一些环境或周边系统的特殊性,业务模式发生转变等,使得内容云成为一个标准的大型遗留系统,早期的一些存储、架构上的设计已经逐渐无法满足当前的业务场景,并给维护者带来了较大维护和迭代成本。
因此我们启动了内容云存储层的迁移项目,随着调研和与其他业务的讨论的不断深入,发现各业务对存储层的痛点及需求基本一致,存储模型和实现方案逐渐趋同,因此决定基于 ByteKV 开发一个宽表数据服务(本文主要聚焦在遗留系统存储层迁移的过程,暂不涉及新存储层的设计与实现细节),下沉存储层通用逻辑,供其他业务接入,并替换内容云原有的存储层。最终历时将近 1 年时间将在线流量切换至新的存储层。
迁移一个系统的存储能有多复杂?无非是双写、迁移数据、切读、停写罢了,为何内容云存储层的迁移竟花费将近一年时间?本文主要分享内容云存储层迁移的血泪史,过程中的一些坑和经验,望能给其他大型系统迁移存储或做重构带来一些流程上的参考。
虽然大体目标是将原存储层替换成新的存储层,但预期本次迁移也需要解决原存储层由来已久的多存储不一致问题、容量瓶颈、主从延迟等问题,这要求在迁移过程中也需要对内容云业务层进行大量改造,将原有业务层中包含的存储层功能下沉到新的存储层,使业务层和存储层边界明确,带来了额外的工作量。
由于原有主要存储为 MySQL,本身数据模型为表格型,而新存储使用 ByteKV,数据模型为 KV 模型,虽然在新存储层建设过程中已经完成了基于 KV 模型提供表格模型能力的开发,但相关功能的能力与旧存储层的能力仍有偏差,需要在迁移过程中不断的完善和进一步改造。
迁移时内容云数据量已经达到数百亿条,主要读服务请求高峰期流量数百万 QPS,大的数据量+大请求量使得在双写、做 diff、刷数据等每个阶段都需要考虑性能问题,资源问题。本身双写双读期间就需要引入额外的资源消耗,使得过程中不得不抽出一些时间优化之前系统的性能,以释放出一些资源进行双写、双读、消 diff 及压测等验证工作。后面会详细介绍两次大的性能优化过程。
唯一不变的是变化,在整个迁移过程中内容云系统也在持续进行迭代,整个迁移的过程如同给正在奔跑的汽车换轮胎,给正在飞行的飞机换发动机,需要做到业务无感。新 feture 的加入需要同时作用到两套存储上,否则就会产生 diff,时刻关注 diff 情况并追齐新加的 feature 同样花费了不少时间。
由于业务经手人数较多,历史悠久,遗留系统中都有众多黑盒及不可解释的逻辑,对这些逻辑的理解及兼容是前期项目计划之外的。此外历史数据的混乱,已经无法用现有系统的标准去度量,为保证切换过程中透明,甚至需要去兼容历史上错误的数据。
内容云本身对存储层的依赖如下图:
此架构主要有以下几个问题:
此阶段主要进行数据双写代码准备,及写 diff 流程、监控的搭建。
从上述存储架构可以看出,上层业务统一通过了抽象接口层(data_source)访问底层的存储,理论上在抽象接口层新增一个宽表数据服务的实现,并把旧存储的实现直接替换为新存储的实现即可完成存储的替换,即基于新存储的依赖如下图所示:
然而,理想很美好,现实总是很骨感,这样虽然能做到替换存储,但并没有达到重构的目的,即解决多存储不一致等问题,之前分别处理多个存储的代码在业务层进行,通过 data_source 中的不同接口进行不同存储数据的操作,因此需要进行 data_source 接口的改造和在线写服务中操作多存储部分的代码改造。同时把写 diff 的流程搭建起来。
此阶段主要开发工作有:
写 diff 过程不管做的多细致都不过分
上阶段代码准备完成后,开始无尽的消 diff 工作,由于内容云字段已经超过 2000 个,需要对有 diff 的字段逐个进行排查,并不断进行代码改造以消除这些 diff,是一个极度需要细致和耐心的过程。
最终写 diff 消除用时 1 个月左右。后面也证明写 diff 阶段不管多细致都不过分,因为写 diff 消除完成证明了数据写入已经没有问题了,可以进行历史数据迁移,如果历史数据迁移完后又发现有写 diff,很可能需要再次全量刷一遍数据,费时费力。然而虽然用时一个月后来发现仍有一些坑,导致大大小小最终刷了不下 10 遍数据,后面说。
下面总结下比较有代表性的写 diff:
a. 字符集配置的 UTF-8,导致本身存储中不支持 emoji 表情,而新存储中支持导致的 diff。
b. 字段类型配置为 tinyint,导致业务上如果写一个较大的值时会发生溢出,而新存储不会。
4 . 两个存储一个成功、一个失败导致的 diff,需要在一个存储失败时进行后续的补偿重试,因此搭建了数据修复流程,期望两存储能够达到最终一致的状态。
5 . 请求乱序,如下图,可能会发生请求 2 比请求 1 先到的情况。需要在写请求之前加锁,并在两存储写完后再释放锁,前提是能确保新存储的性能不会对上游产生影响。
6 . 时间戳问题
由于两存储无法保证准确的同一时刻写入,导致有些时间戳会出现 diff,这种解决方案分两种情况,对于无法接受 diff 的时间戳需要在业务层统一时间戳,再指定使用统一时间戳写入两存储。对于能够接受 diff 的时间戳需要在 diff 时忽略掉。
7 . 序列化问题
一些反序列化方法会把 JSON 中的数字转为 json.Number,这在业务中类型断言或 diff 比较时都会留坑,应尽量在下层处理好这类问题。
8 . 序列化的顺序
由于 map 结构的无序性,在序列化成字符串时会导致顺序不一致,可能在某些业务逻辑中有坑,较好的方法是在序列化时保证进行有序的序列化,已经有许多开源的 JSON 库能够做这样的事情。
9 . 服务本身的异步写入
这种 diff 可能是内容云独有的,之前有较多逻辑直接在写服务写完主存储后,起异步协程再进行一些计算和数据操作,这使得这些写入的请求顺序无法得到保证。较好的做法是把操作存储的逻辑收敛到统一的写服务接口上。
10 . 存储一前一后写入,或一前一后读取导致的 diff
由于无法保证在做 diff 时的事务隔离性(会影响在线服务,不太能接受),会存在在 diff 读取时刚好有并发的数据写入操作,导致的不一致,这种即使延迟一段时间再次进行 diff 也无法完全消除,因此最终 diff 的消除也无法达到 100%的一致率,最终在一致率达到 99.99%时经追查仍有 diff 的 case,发现都属于这种情况,这时认为写 diff 已经消除完成了。
尝试探索更高效的历史数据迁移方案能提升存储迁移的效率,除非能保证只刷一遍数据
经过写 diff 消除阶段,此时理论上新增的数据写入已经没有问题了(只是理论上,后面读 diff 时发现还是有一些边缘 case 导致的写 diff)。这个阶段主要是把历史存量数据从旧存储导入新存储中。这个过程依然基于统一接口层 data_source 实现。
这个阶段同样需要做完备 diff,需要验证导入的历史数据是否符合预期,需要进行历史数据的正确性校验,但当时由于新存储本身资源不足,离线数据也还不支持产出,此时进行历史上 400 亿条数据的对比是无法进行的,因此这个阶段只进行了有明显问题 diff 的修复,把历史数据 diff 的校验工作放到了切读前的最后一步,但更合理的做法是在此时就校验好历史数据的正确性,否则之后可能会产生重复的刷数据工作。
此阶段主要会遇到的问题是如果一些数据是在真实数据写入时生成的,可能有问题,需要新存储支持这些数据可以指定写入,如:
create_time 类数据,是在新数据写入时根据时间戳生成的,但历史数据的 create_time 不能使用刷数据时的时间,因此需要新存储支持上游指定写入 create_time 的值,进行一些代码改造。
刷数据的工作主要是依赖业务上层的实现进行,因此刷数据的过程需要进行大量的计算逻辑,是比较低效的,理论上把刷数据的工作越下沉越高效,比如参考 MySQL 迁移数据时的文件级别拷贝等。由于当时考虑内容云迁移本身 1. 数据导入速度不会成为整个项目的瓶颈 2. 新旧存储数据模型差别过大,通过离线数据导入也需要大量适配、验证工作,当时并没有考虑更加高效的存量数据迁移的方案,后期刷全量数据约需要 5 天时间,但在存储迁移的过程中如果能把数据迁移的时间压缩到比较短,如半天能完成存量数据的全量迁移,对整个迁移工作是比较有利的,可以进行快速的验证和试错。
性能优化初见成效
在历史数据迁移的过程中,我们也对新存储层的性能进行了又一次压测,发现在数据写入 QPS 到达 3w 时,基本就会把 ByteKV 打挂,虽然此时只有部分机器资源到位,但也开始对性能产生深深的担忧,因为此时压测比四月份的压测更接近真实业务场景。按照此时的压测数据来看,即使到了开始预估的全量机器,也很可能无法承接所有流量。因此在七月份开启了缓存的优化改造,主要两点考虑:
主要缓存优化的思路是根据内容云实际业务场景出发,发现之前使用缓存的方式存在很大浪费,优化思路可能并不能直接复用于其他业务,这里不详细展开介绍。但值得注意的是对于类似大型遗留系统由于业务历史上的转变,总会发现一些系统中不合理的点,经过简单优化后可能能得到意想不到的收获。
简单说下此阶段主要进行了两点业务上的优化:
因此,对在线读服务的缓存进行了改造,合并了多份缓存的数据,并且把两层缓存改为一层,从而释放出了 Redis 资源供新存储使用,此次优化后缓存命中率得到提升 90%->98%,且节省出的缓存空间足够新老两套存储同时使用。
经过缓存的优化,对新存储加上缓存再进行压测,此时的压测数据基本可以保证如果预期资源能如约到位,ByteKV 是基本能够承担内容云的所有流量的。
每个 diff 的消除都是在解决切换过程中的隐藏炸弹,diff 越仔细,切流时越安心
与很多业务中台一样,内容云的读服务在读取数据之后进行一些计算打包逻辑,此阶段主要对内容云业务层两个出口服务的读接口进行 diff 流程搭建和消除工作。对于读服务来说进行了一些重构,预期把老的回源服务下掉以保持整体架构的简洁。服务改造图如下:
主要改造点:
相比于写 diff 阶段,读 diff 需要消除的 diff 并不算多,更多的 diff 是由于部分需要重构和适配的逻辑与原逻辑没有对齐导致的,但由于读接口流量较大,一般无法打印比较详细的日志,导致对于 diff 的排查工作较难进行,常常需要根据数据和代码的蛛丝马迹在脑中进行编译执行来定位具体产生 diff 的原因,这里也是极度需要耐心和细致的过程。
终于找到 ByteKV 的正确打开方式!
读 diff 消除完成后,理论上已经可以进行逐步切流至新存储,但意外总是不期而遇,最早预估的机器资源由于整体资源紧张并没有如期到位,导致此时新存储的资源不能承担所有流量。因此需要进行进一步的性能优化。
在一次小的性能优化上偶然发现,写数据时把每次写存储的 Key 数量缩小一半,性能不止能翻一倍。基于尽量减少 Key 的个数这个思路开始进行代码的重构和调整(当然又需要全量刷一遍历史数据),主要进行了两点优化:
经过上次两点优化,保证了对于大部分请求读写一篇文章的数据,能够保证读写新存储 4-5 个 Key 即可完成,这使得一切变得美好起来,接口的延迟能够稳定保持在 10ms 以下,错误率也不会像之前那样有突刺了。经过优化之后再压测,当前的机器已经足以承担所有流量,甚至还有富裕。
做字段合并是基于内容云的历史包袱和整体资源不足的无奈之举,虽然提高了性能,但也会在其他场景引入坑,如非必要请勿作此妥协。
对于历史上的脏数据如果无法兼容,尝试把它改对吧
你永远无法想象一个历史遗留系统中的数据能有多混乱,历史数据的混乱总在不断的颠覆对内容云这个系统的认知。如:
每次发现这种问题都仿佛是跟前人的一次对话,慢慢可以理解或者想象当时发生了什么事情,如可能某几天线上有 bug,造成脏数据,但并不影响整体使用,逐渐的这些脏数据也就留在了遗留系统中。
前期为了保证切换存储对上游完全透明,即对于这些脏数据我们也想办法尽可能让他继续保持现状,然后随着兼容的脏数据越来越多,发现我们新写的逻辑逐渐不可解释和维护,最终痛定思痛决定还是按照合理的方式把脏数据变成本来该有的样子(又进行了一遍全量刷数据),最终结果发现把历史上的脏数据改对可能确实是正确的,上游也没有依赖脏数据做逻辑,切换无感知。
切流量是一个漫长、危险,如履薄冰的过程,需要保证每一步可回滚,可快速恢复
经过前面的阶段,已经基本保证了新存储读、写的功能和性能满足要求,在 12 月份终于迎来了切量到新存储。由于此时一些旧存储的调整导致此时旧存储的主从延迟问题更加严重,导致业务上反馈较多,因此选择优先把主要读服务切换到新存储上。
此步骤主要就是把读接口流量切换到新的链路来承接,本身开发工作不大,主要是需要观察切量过程中是否有问题,切量前后的系统流量,稳定性等是否满足需求,同时需要做好线上问题的处理预案。保证任何时候出现问题能够快速回滚,及时止损。
最终历时三周时间,把在线读服务的所有流量切换到新的链路上,彻底告别了主从延迟导致的数据不一致问题。
在线流量切换完成。需要做好切换过程中的数据补偿
切主存储,主要是写入相关接口,之前还是以旧的存储作为主存储,旧存储成功即返回成功,旧存储失败接口返回失败。需要切换到以新存储返回为准。需要注意的是需要做好数据补偿,如切之前,旧存储成功,新存储失败,需要利用旧存储的数据尝试修复新存储的数据,切完之后,新存储成功,旧存储失败需要利用新存储的数据尝试修复旧存储的数据,需要保证切换过程平滑可回滚,不会出现数据不一致的 badcase。如下图,把新存储切为主依赖,旧存储切成弱依赖。
最终又历时两周,切主存储完成,在线流量全部切换到新存储上,整个项目完成。
新的存储层基于强一致的 ByteKV,不会产生一篇文章部分属性写成功,部分写失败的问题,切换后消除了不一致问题的反馈。
迁移中附带解决了业务中的一些历史包袱,对历史不一致脏数据尝试修复,明确业务层和存储层的边界。使整体系统架构更加清晰。
存储层可用性 99.8->大于 99.99%。
新存储支持了大 Key 的拆分,解决 MySQL 单列存储上限问题,满足部分业务对单列大容量存储的需求。
将 MySQL 替换为计算、存储分离的 ByteKV,使得存储容量不再是存储层的瓶颈。
同样得益于 MySQL->ByteKV, 切换后无主从延迟导致的缓存脏数据问题反馈。
本文从存储层迁移流程的角度详细阐述了大型系统存储迁移的过程,分析了其中的难点和过程中的一些坑,总结来说过程中也有一些不足和感悟:
希望能给其他系统做数据或存储层迁移重构带来一些帮助或参考,能够更加快速、安全的进行存储或数据的迁移工作。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/xrvzIQv2HoGSg5T2NceRAg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。