一次MongoDB迁移之后,我开始重新理解融合数据库
文章目录
- 一次MongoDB迁移之后,我开始重新理解融合数据库
-
- 一、烟囱式架构的问题,往往在业务变复杂之后才显现
- 二、MongoDB 迁移,先要弄清楚迁移的对象是什么
-
- 一个小型数据画像脚本
- 三、KingbaseES 的融合思路,解决的是迁移之后的问题
- 四、我会怎样设计一次可回退的 MongoDB 迁移
- 五、迁移之后,才是融合数据库真正发挥价值的时候
我头一回认认真真去搞 MongoDB 迁移的时候啊,其实心里最担心的,不是这数据到底能不能导过去。我担心的是什么呢?是迁移弄完之后,业务会不会在某个没测到的角落,突然就变慢了。很多人觉得迁移嘛,也就是一次导入导出。把集合给读出来,转成目标库能认的格式,然后再写进去。真正干过项目的人肯定都知道,迁移从来就不是“搬家”这么简单的事儿。它同时牵扯到什么呢?数据模型、应用怎么去访问、索引、平时运维的习惯、出了故障怎么恢复,还有以后怎么扩展。
这也是我后来开始去关注 KingbaseES 的 MongoDB 迁移能力的一个原因。我们去迁 MongoDB,目标其实不应该是仅仅把原来的数据塞进一个新的容器里就完事了。而是说,得借着这个机会,把企业层级里的数据底座给重新梳理一下。你得搞清楚,哪些是交易数据,哪些是文档数据,哪些得拿去做空间查询,还有哪些以后可能要跑向量检索的。如果这些数据一直靠好多套独立的系统去扛着,那系统肯定会越来越多。数据同步的链路呢,也会跟着越来越长。迁移的价值啊,最后得落在哪儿呢?得落在更好管理、更好扩展,还有更好去做融合分析这些事情上。
当同步链路都已经变成业务系统里面的一部分的时候,你迁移的目标啊,就不能光看目标库能不能把源数据收进去了。你还得看,迁移完之后,这条链路到底有没有变短。
一、烟囱式架构的问题,往往在业务变复杂之后才显现
在好多企业早期的架构里面啊,往往是 MySQL、MongoDB、Redis 还有其他的一些专用组件,每个都去干一类活儿。这样的组合其实并不是说天生就有错。业务量还小的时候,它甚至挺灵活的。但是呢,一旦系统跑的时间长了,问题就会一点点都冒出来了。 
第一类问题呢,就是运维的成本。每一套数据库,它都有自己的备份办法、要看的监控指标、升级的窗口、权限的体系,还有出了故障怎么去处理。你新增一个业务系统,看着好像就是多加了一套服务。实际上呢,你还多加了一堆管理上的边界。管数据库的人啊,得分别去搞懂不同的连法、不同的索引规则,还有各种参数怎么配。写应用的团队呢,也得去维护好几套客户端,还有访问数据的代码。
第二类问题,就是数据的一致性。订单啊、设备资料啊、用户画像还有操作日志,这些东西散落在好几个系统里面之后。业务要是想做一次完整的分析,通常来说得先去同步,接着去清洗,然后再去关联。同步的任务啊,真不是越多越好的。链路要是拉得太长了,延迟就会变大,出错的点也会变多。某一张表的数据都已经更新了,但是另一套系统还没同步完。这时候报表和在线服务看到的东西,可能就根本不是同一个事实了。
第三类问题呢,是数据的价值被割开了。文档的信息存在文档库里面,业务的属性存在关系表里面,地理位置又在空间系统里面。后面可能还得加上向量数据。应用为了拼出一个完整的答案,就只能在不同数据库之间来回搬数据。搬数据不仅浪费资源,而且会让权限怎么控制、怎么审计、出了问题怎么去定位,这些都变得特别复杂。
我其实并不觉得“把所有数据都塞进一张表里面”是一个合理的目标。更现实的一个目标是什么呢?就是让不同的数据模型,能够在同一个可以治理的数据底座里面待着。而且到了需要关联的时候,可以用同一种访问方式,把查询和分析给做完。
二、MongoDB 迁移,先要弄清楚迁移的对象是什么
在迁移之前啊,最容易犯的一个错,就是光去数有多少个集合、有多少个文档。真正需要去盘点的东西,其实至少得包括四个层次。 
第一个是数据层。你得去确认集合的字段是什么类型,嵌套有多少层,有没有空值的情况,时间字段是什么格式,还有历史数据里面有没有什么异常的值。文档数据库那种灵活的模式啊,用起来确实方便。但是灵活也就意味着,同一个字段在不同时间写进去的时候,可能类型都不一样。迁移的时候,如果你光看着几个样例文档就去设计目标的结构,那往往会把历史数据里面的一些边界情况给漏掉。
第二个是访问层。你得把应用用过的查询条件、怎么排序的、怎么分页的、怎么更新的,还有批量操作,全都给列出来。迁移啊,不能光看数据长什么样,还得看应用实际上是怎么去用这些数据的。打个比方,某个字段写进去的时候并没有唯一约束,但是应用代码里面默认它就是不会重复的。又或者某个数组字段看着好像不太重要,但其实是查询过滤条件里面的一部分。这些信息啊,比“这个集合到底有多少列”更要紧,它们才决定了迁移完之后兼不兼容。
第三个是索引层。索引这个东西啊,得从业务的查询出发去重新确认。原来系统里面的索引,可能就是为了以前某个老版本的查询建的。也可能有一些索引放了很久,根本就没谁用过。迁移的时候呢,应该把索引分成三类。就是必须留下的、需要重新建的,还有可以直接淘汰掉的。然后再拿典型的查询去跑一下,看看执行计划对不对。而不是说傻乎乎地把所有的索引都原样搬过去。
第四个是运行层。你得搞清楚迁移的窗口在哪,增量同步怎么搞,回退的方案是什么,校验的口径是什么,还有切换的时候到底谁负责。数据量要是很大的话,一次性停机导入其实并不适合所有的场景。更稳当的一个做法通常是什么呢?先去把全量迁移给弄完,接着持续去同步增量的部分,最后挑一个低峰期的时间段,花很短的时间把切换给做了。不管你用的是什么工具,在切换之前,你都得明确一句话:“到底什么情况才算迁移完成了”。 我一般会先用下面这张表,把“迁移对象”给具体化。它可比那种光写着集合名字和数据量的清单有用多了。
| 文档结构 | 字段类型、嵌套层级、空值、异常样本 | 关键字段类型和业务含义一致 |
| 访问方式 | 查询、排序、分页、批量更新 | 典型接口结果和边界行为一致 |
| 索引 | 索引字段、基数、使用频率 | 关键查询有稳定执行计划 |
| 运行任务 | 定时作业、报表、备份、告警 | 切换后任务不再访问旧连接 |
| 数据安全 | 账号、权限、脱敏、审计 | 越权访问和敏感字段暴露均被拦截 |
一个小型数据画像脚本
const sampleSize = 2000;
const collectionName = "device_events";
const c = db.getCollection(collectionName);
printjson({
collection: collectionName,
count: c.estimatedDocumentCount(),
indexes: c.getIndexes().map(x => ({ name: x.name, key: x.key }))
});
c.aggregate([
{ $sample: { size: sampleSize } },
{ $project: {
_id: 0,
fieldTypes: {
device_id: { $type: "$device_id" },
event_time: { $type: "$event_time" },
payload: { $type: "$payload" }
}
} },
{ $limit: 20 }
]).forEach(printjson);
这个脚本啊,它只去读集合,重点是干一件什么事呢?就是把字段类型的漂移情况、索引现在长什么样,还有数量的大概估算,变成可以交接出去的证据。如果集合里面含有敏感的字段,那就只输出类型,绝对不输出原始的值。
三、KingbaseES 的融合思路,解决的是迁移之后的问题
从产品的定位来看的话,KingbaseES 的价值其实并不只是给你多提供了一个存关系数据的地方。KES-AI 时代融合数据库架构,它强调的是在一个统一的底座里面,去处理关系数据、文档数据、向量数据、GIS 数据还有时序数据这好几种模型。

那对于 MongoDB 的迁移项目来说,这种思路的意义在哪儿呢?就在于原来那种以文档形式存着的数据,在迁移完之后,还能继续保持应用需要的那种灵活性。同时呢,它还能跟关系的业务数据、空间的信息,还有后面要加的智能检索能力,都放在同一套可以治理的边界里面。
这里得特别说明一下,“多模融合”这句话啊,并不是说要把所有的数据都强行改成一个样子。文档数据嘛,还是可以按文档的模型去管。关系数据呢,也还是适合用表、用约束、用事务去搞。向量数据,也得用那种适合做相似性检索的类型和索引。融合的重点其实是把存储、权限、备份、运维还有关联分析的边界给统一起来。把那些没必要的数据同步,还有重复去建的东西给减少掉。
如果说项目的应用访问方式跟兼容能力刚好能对上,那应用迁移的时候就可以尽量少改点代码。资料里面提到的那种“0代码修改完成应用迁移”啊,你更适合把它当成一个兼容性的目标去理解。而不是说连测试都不做,就敢拍着胸脯保证所有应用都不用调。实际的项目里面啊,你还是得去查一查驱动对不对、连接参数行不行、有没有什么特殊的语法、事务的边界在哪、分页的行为一不一致,还有异常是怎么处理的。迁移工具确实能让自动化的程度变高。但是呢,业务的验收这一步,你依然是不能给省掉的。
四、我会怎样设计一次可回退的 MongoDB 迁移
我自己的做法啊,通常来说会分成这么五步。 
第一步呢,是把迁移清单给建起来。这个清单啊,它不光要记下数据库和集合。它还得记清楚数据是谁负责的、数据量有多大、增长的速度有多快、读和写的比例是多少、业务的等级是什么、允不允许停机,还有验收的指标是什么。要是没有这个清单就去迁移,你很容易在最后关头才发现,原来还有个边缘的服务在死死依赖着旧的库。
第二步,去做数据画像。抽样的时候啊,你不能光抽那些最新的数据。你得把早期的历史数据、峰值时候的数据,还有那些奇奇怪怪的异常数据都给覆盖到。对字段类型、编码、时间的精度、空值还有嵌套的结构去做统计。弄出一份可以重复去跑的校验报告出来。如果目标库需要把文档的结构重新组织一下,那你得把转换的规则白纸黑字写下来。而不是说,就靠写脚本那个人脑子里的记忆。
第三步,去搞全量迁移还有增量同步。全量那个阶段啊,你盯住吞吐量、失败了怎么重试,还有日志就行了。增量那个阶段呢,你得盯住顺序对不对、有没有重复写进去的情况,还有断了能不能接着传。迁移的脚本啊,应该得能够重复去执行。如果某一小批失败了,你得能马上定位到是哪一批、哪几条记录出了问题。不能说因为网络稍微抖了一下,你就只能哭丧着脸从头再来。
第四步,做双向的验证。光去对一下数量啊,这仅仅是最低的要求。你还得按业务的主键、按时间的范围,再去抽样算一下哈希值,把关键的字段给校验了。对于那些里面有嵌套数组的、有长文本的,还有引用了二进制的数据,你得单独去验证它完不完整。应用那边呢,要把真实的查询给回放一遍。去比一比结果集一不一致、排序对不对、分页有没有问题,还有响应时间差多少。
第五步,就是切换还有观察。在切换之前啊,你得把变更给冻住,把旧系统保留在一个能读的状态,然后把最后同步的位点给记下来。切完之后呢,先别急着重负载。你先去观察核心的接口、后台跑的任务、报表还有审计的日志。看着没啥问题了,再一点点把流量放上去。只有当新系统跑得稳稳当当的,备份也能恢复了,回退的条件也不再被触发了,这时候你才去考虑把旧系统给下线。
#mermaid-svg-seyMuEysyZeNT97W{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-seyMuEysyZeNT97W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-seyMuEysyZeNT97W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-seyMuEysyZeNT97W .error-icon{fill:#552222;}#mermaid-svg-seyMuEysyZeNT97W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-seyMuEysyZeNT97W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-seyMuEysyZeNT97W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-seyMuEysyZeNT97W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-seyMuEysyZeNT97W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-seyMuEysyZeNT97W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-seyMuEysyZeNT97W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-seyMuEysyZeNT97W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-seyMuEysyZeNT97W .marker.cross{stroke:#333333;}#mermaid-svg-seyMuEysyZeNT97W svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-seyMuEysyZeNT97W p{margin:0;}#mermaid-svg-seyMuEysyZeNT97W .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-seyMuEysyZeNT97W .cluster-label text{fill:#333;}#mermaid-svg-seyMuEysyZeNT97W .cluster-label span{color:#333;}#mermaid-svg-seyMuEysyZeNT97W .cluster-label span p{background-color:transparent;}#mermaid-svg-seyMuEysyZeNT97W .label text,#mermaid-svg-seyMuEysyZeNT97W span{fill:#333;color:#333;}#mermaid-svg-seyMuEysyZeNT97W .node rect,#mermaid-svg-seyMuEysyZeNT97W .node circle,#mermaid-svg-seyMuEysyZeNT97W .node ellipse,#mermaid-svg-seyMuEysyZeNT97W .node polygon,#mermaid-svg-seyMuEysyZeNT97W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-seyMuEysyZeNT97W .rough-node .label text,#mermaid-svg-seyMuEysyZeNT97W .node .label text,#mermaid-svg-seyMuEysyZeNT97W .image-shape .label,#mermaid-svg-seyMuEysyZeNT97W .icon-shape .label{text-anchor:middle;}#mermaid-svg-seyMuEysyZeNT97W .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-seyMuEysyZeNT97W .rough-node .label,#mermaid-svg-seyMuEysyZeNT97W .node .label,#mermaid-svg-seyMuEysyZeNT97W .image-shape .label,#mermaid-svg-seyMuEysyZeNT97W .icon-shape .label{text-align:center;}#mermaid-svg-seyMuEysyZeNT97W .node.clickable{cursor:pointer;}#mermaid-svg-seyMuEysyZeNT97W .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-seyMuEysyZeNT97W .arrowheadPath{fill:#333333;}#mermaid-svg-seyMuEysyZeNT97W .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-seyMuEysyZeNT97W .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-seyMuEysyZeNT97W .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-seyMuEysyZeNT97W .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-seyMuEysyZeNT97W .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-seyMuEysyZeNT97W .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-seyMuEysyZeNT97W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-seyMuEysyZeNT97W .cluster text{fill:#333;}#mermaid-svg-seyMuEysyZeNT97W .cluster span{color:#333;}#mermaid-svg-seyMuEysyZeNT97W div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-seyMuEysyZeNT97W .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-seyMuEysyZeNT97W rect.text{fill:none;stroke-width:0;}#mermaid-svg-seyMuEysyZeNT97W .icon-shape,#mermaid-svg-seyMuEysyZeNT97W .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-seyMuEysyZeNT97W .icon-shape p,#mermaid-svg-seyMuEysyZeNT97W .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-seyMuEysyZeNT97W .icon-shape .label rect,#mermaid-svg-seyMuEysyZeNT97W .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-seyMuEysyZeNT97W .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-seyMuEysyZeNT97W .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-seyMuEysyZeNT97W :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
不通过
通过
否
是
冻结迁移范围
数据画像与对象映射
全量迁移
数量/哈希/业务查询校验
修正规则并重跑批次
开启增量同步
回放真实接口和报表
性能与一致性达标?
低峰切换
观察窗口
确认完成并保留回退点
我一般会把切换的门槛,写成那种可以观察到的条件。比如说,关键的接口连续看下来没有错误。增量的位点已经追平了。核心的集合抽样出来是一致的。P95 的延迟没有超过基线允许的那个范围。还有备份恢复的演练也跑通了。具体的数值是多少呢,这个得由项目自己来定。我在这儿啊,就不替项目去瞎编一些数字了。
五、迁移之后,才是融合数据库真正发挥价值的时候
迁移啊,它并不是这个项目的终点。文档的数据进到统一的底座里面之后,企业其实就可以慢慢把以前那种靠 ETL 去跑的分析链路给收回来了。打个比方啊,设备的文档跟设备的台账,现在就可以在库里面直接关联了。位置的数据呢,可以拿去跟业务的事件做空间分析。文档的内容被嵌入之后,也可以跟业务的条件混在一起,去跑相似性检索了。你每少搬一次数据跨库,就少了一个同步会延迟的点,也少了一个会失败的地方。 
不过这并不代表说,所有的计算都得硬塞到数据库里面去完成。也不代表说,不管什么场景都适合马上合在一起。架构怎么选啊,你还是得看数据到底有多大、团队的能力够不够、监管有什么要求,还有业务的目标是什么。KingbaseES 的优势啊,你更适合从“统一管理还有减少系统拼接”这个角度去评价它。而不是说,光去比某一个孤立的接口它跑得有多快。 还有一个很容易被忽略掉的情况,就是你去权衡一下。如果说业务仅仅只是临时去采个日志,数据的生命周期又特别短,而且团队里面本来就已经有一套很成熟的 MongoDB 运维办法了。那你要是为了所谓的统一去迁移,这未必划算。那真正值得去迁移的信号是什么呢?通常是这样的:同一批数据,它需要跟交易数据、空间数据或者知识数据反反复复地去关联。或者同步的链路已经把实时性给拖慢了。又或者好几套数据库的权限和备份,你已经审计不过来了。再或者,业务那边开始要求你要有统一的高可用还有灾备的策略了。
现在回头去看那次 MongoDB 迁移啊,我最大的收获其实并不是记住了一堆迁移的命令。而是我意识到了一件事:数据库迁移,它其实就是一场对数据的关系还有业务的边界,重新去做一次盘点。你去选 KingbaseES 这样的融合数据库,它的价值也不仅仅是把数据库的牌子给换一下。而是说,让迁移完的数据少几个孤岛。多一些可以治理的、可以关联的、还可以一直往下演进的空间。只要你能把兼容性的验证、数据的校验还有回退的机制给做扎实了。那 MongoDB 迁移,就可以从一次被动的替换,变成一次有计划的数据底座升级。
网硕互联帮助中心



评论前必须登录!
注册