当前位置:首页 > 问答 > 正文

MongoDB数据库重构那些事儿,聊聊怎么改才能更顺手点

说到MongoDB数据库重构,这其实是个挺常见但又容易让人头疼的事儿,项目刚开始的时候,为了求快,文档结构可能设计得比较随意,等业务跑起来,数据量上来了,各种问题就冒出来了,比如查询慢得像蜗牛、数据重复不好维护、或者想加个新功能发现现有结构根本支持不了,这时候,就得考虑动手改一改了,也就是我们常说的“重构”,但改数据库不像改代码,它牵扯到已有的数据,得特别小心,下面我就聊聊怎么改才能更顺手、更稳妥。

MongoDB数据库重构那些事儿,聊聊怎么改才能更顺手点

最重要的一点是:想清楚再动手,别蛮干。 在开始任何改动之前,一定要花时间彻底分析现状,你得弄明白当前的结构到底哪里不好,是因为嵌套太深,查询总是要展开一大堆用不上的数据吗?(来源:MongoDB官方博客关于数据建模反模式的讨论)还是因为把本该放在一个文档里的信息,拆得到处都是,导致一个简单的查询要跑好几次数据库?(来源:社区中常见的“过度规范化”问题讨论)又或者是字段名起得乱七八糟,连自己时间长了都看不懂?先把痛点找准,然后设定清晰的改造目标,将查询用户订单详情的响应时间从500毫秒降低到100毫秒以内”,有了明确的目标,后续的工作才有方向,也能用来衡量重构是否成功。

优先考虑“非破坏性”的改动。 这是让重构过程更顺手的关键策略,啥叫非破坏性?就是你的改动不会立刻把旧的代码搞垮,最典型、最推荐的第一步就是“增加字段”和“数据双写”,比方说,你原来把用户的地址信息直接嵌在用户主文档里,现在觉得不好管理,想单独拿出来放到一个叫addresses的数组里,你别急着把旧的address字段删了,可以这样做:

MongoDB数据库重构那些事儿,聊聊怎么改才能更顺手点

  1. 先加后弃:先创建新的addresses数组字段,然后修改应用程序的“写”逻辑,变成“双写”:既更新旧的address字段(为了保证现有代码还能运行),同时也更新新的addresses数组。
  2. 迁移数据:写一个后台脚本,慢慢地、分批地把所有用户文档里旧address的数据,迁移到新的addresses结构里,这个脚本可以放在业务低峰期跑,对线上服务影响小。
  3. 切换读操作:等数据迁移得差不多了,确认新字段的数据是完整准确的,再把应用程序里所有“读”地址的逻辑,从读旧的address字段改为读新的addresses数组。
  4. 清理旧字段:确认所有代码都已经切换到新字段,运行稳定一段时间后,再写一个脚本把旧的address字段从所有文档中删掉,甚至可以等到下次大版本更新,彻底不再支持旧字段时再删。

这种方法就像修路时的“修建辅道,再改造主路”,保证了交通(你的服务)始终能正常运行,心里特别有底。

善用MongoDB自身的工具和功能。 MongoDB提供了一些内建的功能来辅助重构。聚合管道(Aggregation Pipeline)功能非常强大,不仅能用于复杂查询,在数据迁移和转换时也是利器,你可以用$set$unset$merge等聚合阶段操作,非常灵活地在数据库层面完成文档结构的调整和数据的重新组织,比自己在应用层写脚本有时更高效。(来源:MongoDB官方文档关于使用聚合管道更新数据的案例)如果改动很大,涉及到集合的合并或分片等,MongoDB Connector for BI工具可以帮你更方便地将数据导出到其他分析工具中进行验证和处理。

还有,测试和回滚方案绝不能省。 数据库重构的风险很高,所以测试必须充分,不仅要测试正常流程,还要重点测试边界情况和异常场景,更重要的是,一定要提前准备好回滚方案,万一新上线的结构有问题,你能多快恢复服务?回滚的脚本要提前写好并测试过,如果你删除了一个旧字段,但发现新逻辑有严重Bug,如果你的回滚方案是“从备份恢复”,那停机时间可能就太长了,如果之前采用了“双写”策略,回滚就会简单很多,只需要把读操作切回旧字段即可。

小步快跑,分批发布。 不要试图一次完成所有重构,把大的重构目标拆分成多个小的、可独立发布和验证的步骤,可以先重构一个相对独立的模块,或者先针对某一种类型的查询进行优化,每次只改动一小部分,然后立即观察监控指标(如数据库性能、应用错误日志等),没问题后再进行下一步,这有点像敏捷开发里的迭代,能及时发现问题,控制风险。

MongoDB数据库重构不是洪水猛兽,核心思路就是“稳”字当头,通过充分分析、采用非破坏性改动、利用好工具、做好测试和回滚准备,再以小步迭代的方式推进,就能让这个过程变得顺顺手手,最终让数据库结构更健壮,更好地支撑业务发展。

MongoDB数据库重构那些事儿,聊聊怎么改才能更顺手点