作者:Daniel Coupal和 Rick Houlihan
发布日期:2023 年 3 月 15 日
经济波动导致商业环境不可预测,迫使组织进一步收紧支出,用更少的资源做更多的事。每一笔投资都得经过严格审查,项目负责人则竭力从现有资源身上获取全部产能。IT 支出引人担忧,许多 IT 决策者不清楚哪个环节导致成本上升。是不合理的高配置?还是云无序扩张?抑或是影子 IT(即未经批准使用软件、硬件或其他系统和服务)?遗憾的是,对于数据在数据库中如何建模,人们却没有给予足够重视。而数据建模恰恰又会从多个方面产生重大影响,如数据库的运营成本高低、处理工作负载需要的实例配置、开发和维护应用程序需要的工作量等。
帕累托模式 (Pareto pattern)
数据访问模式往往能够证明帕累托法则的确在起作用,即,少数原因驱动着多数结果。现代 OLTP 应用程序处理的往往是小区块的数据。大多数数据访问模式(应用程序访问和使用数据的方式)要么处理单行数据,要么处理单张表格中的多行数据。在对我们部署的、基于 RDBMS 的多样服务中 10000 项服务进行评估后,我们发现亚马逊与此情况吻合。标准化数据模型能够高效处理这些简单的单表查询,但是不常见的复杂模式需要数据库联接表来生成结果,这暴露了 RDBMS 效率低下。与这些查询相关的高时间复杂度意味着需要更多的基础设施支持。
关系数据库会将大量此类额外成本隐藏在幕后。当你将向关系数据库发出查询时,实际上并不能够真正查看全部表格上打开的所有连接,或合并的所有对象。尽管亚马逊上 90% 的访问模式都是针对简单事务,而 10% 的用户在进行更加复杂的访问时候却消耗了惊人的 CPU。据本团队估计,后者的消耗量之大导致基础设施成本升高了约 50%。此时就需要 NoSQL 数据建模来发挥颠覆性作用。NoSQL 数据模型经过专门设计,能够消除昂贵的联接表、降低 CPU 占用率、节省计算成本。
利用 NoSQL 提升建模效率
在 NoSQL 数据库中对关系数据进行建模的两种基本方法:
1.嵌入式文档 - 所有相关数据都会存储在单一富文档中,可在需要时高效检索该文档。
2.单一集合 - 相关数据可拆分为多个文档,从而高效支持需要用到较大型关系结构中子集的访问模式。相关文档存储在一个公共集合中,并包含可供索引的属性,以支持对相关文档的各种分组的查询。
要打造高效 NoSQL 数据模型并降低计算成本,关键在于要使用工作负载来影响数据模型的选择。例如,读取密集型工作负载,如执行类似“获取产品全部数据”或“获取本类别所有产品”查询的产品目录,可从嵌入式文档模型获益,该模型能够避免因读取多个文档而产生的额外成本。
另一方面,写入密集型工作负载,其写入的是零碎更新的较大关系结构的各个部分,将较小文档存储在单一集合中能够提升运行效率。这类单一集合可供独立访问和索引,在需要所有数据时支持高效检索。最终的选择取决于写入模式的频率和性质,以及是否存在并行运行的高速读取模式。如果是读取密集型工作负载,就需要一次读取尽可能多的文档。对于写入密集型工作负载来说,需要避免文档一有更改就得重新写入完整文档。
联接提升时间复杂度。在 NoSQL 数据库中,视访问模式组合而定,关系表中所有行可能会存储在单一嵌入式文档中,也可能会存储为按索引连在一起的一个集合中的多个文档。将多个相关文档存储为一个集合就不需要联接。只要是在常见范围的多个文档间索引,就能够非常高效地查询相关文档。
例如,面对在一个关系数据库中联接三张表格的查询,机器需要完成其中的 1000 个查询。此时,需要从多张表格读取至少 3000 个对象才能满足这1000个查询的要求。文档模型将所有相关数据嵌入一个文档之后,查询就只要从单一集合中读取 1000 个对象。同样地,机器不必合并三张表格中的 3000 个对象,只需从一个集合中读取 1000 个对象,如此,就不需要功能更强大、费用更高的实例。关系数据库并不能提供很好的控制能力。有些查询会产生大量联接,导致时间复杂度更高,换句话说,需要更多基础设施才能支持工作负载。
减少主要限制
在 NoSQL 数据库中,人们希望产生较高成本的要害环节能够实现最高效率的数据建模。分析查询的频率一般较低。100 毫秒内或是 10 毫秒内回传查询结果都不是最紧要的,用户只想要一个答复。对于每小时、每天或每周运行一次的事务来说,执行效率不及标准化关系数据库也没关系。一秒执行成千上万笔事务的事务性工作负载则需要尽可能提高处理效率,因为此时能够节省的潜在成本要大得多。
部分用户尝试运用这些数据建模技术来提高 RDBMS 平台的效率,因为大部分平台现在都支持类似 MongoDB 的文档结构。这个做法可能适用于工作负载的小型子集。但是,列式存储专为相同大小但相对小型的行而设计。这种存储方式的确很适合小型文档,但当关系数据库中行尺寸增加时,就需要行外存储。在 Postgres 中,这就是所谓的 TOAST(超尺寸属性存储技术)。这个做法会将数据存放在两个位置以避开尺寸限制,然而,过程中的性能也会降低。现代 RDBMS 平台所使用的、基于行的存储引擎在设计时就并未考虑到大型文档,因此,无法对其进行配置以高效存储大型文档。
绘制关系图
对数据建模时,我们建议先通过几个关键问题来确定工作负载的特征:
- 工作负载的性质是什么?
- 实体-关系图是怎样的?
- 有哪些访问模式?
- 每个模式的速度是多少?
- 需要得到优化的最关键查询在哪里?
识别实体以及相互间的关系将为数据模型打下基础。这个步骤完成后就可以开始提取访问模式。如果是产品目录类的读取密集型工作负载,用户很可能需要处理大型对象,这点无伤大雅。有大量用例可供参考。然而,如果处理的是较复杂的访问模式,需要独立访问或更新零碎的较大型关系结构时,那么就需要将数据分成较小型文档,如此才能够高效执行这些高速更新。我们的 MongoDB University 课程 M320:MongoDB 数据建模里会教授前述这些技术。
处理索引
使用适用于高频率模式的索引能够享受到最佳性能。没有索引,就必须读取集合中的每一个文档,观察并确定哪一个文档匹配查询条件。索引是提供快速解析的 B 树结构,能够识别出匹配查询所指定之索引属性相关条件的文档。
出于多种原因,用户可能会选择不对不常见模式进行索引。所有索引都会产生成本,因为只要文档一更改,就必须作出相应更新。用户将面对按一致方式执行的高速写入模式,以及最终会发生的低速读取。这种情况下,用户会接受读取查询的完整集合扫描所产生的较高成本,而不是每次写入更新索引所产生的成本。如果 1 秒写入集合 1000 次且每天只读取 1 次,用户最不应该做的就是为保证读取的效率而针对每一次写入添加索引更新。当然,还得由工作负载决定。总的来说,应当为高速模式而创建索引,在某种程度上(全部或部分)索引应当覆盖频率最高的访问模式。
请注意,不论是经常读取,还是完全不读取,索引都会产生成本。请始终记住,定义索引时必须有充分理由,且这个充分的理由应当是高频率访问模式需要利用此索引来确保高速读取数据。
数据建模和开发人员工作效率
即便已经优化了自己的数据模型,用户还是发现可以在下游再实现成本节省,因为开发人员能够找出比关系数据库更高效的方法来开发、迭代和维护系统。特定文档设计模式和 NoSQL 的特征能够降低维护额外成本,很多时候还能够一并消除维护任务。例如,类似 MongoDB 的文档数据库支持灵活的架构,如此就能够消除与架构迁移相关的维护时段,也不必重构与 RDBMS 一样的目录。关系数据库中的架构变化通常都会影响到 ORM 数据适配器,都需要对其进行重构以适应变化。这就会给开发人员造成惊人的代码维护工作。
有了类似 MongoDB 的 NoSQL 数据库后,就不必再配置繁琐且脆弱的 ORM 抽象层。开发人员能够存储采用原生格式的对象数据,不必对数据执行标准化就能生成表格模型。在 MongoDB 中更新数据对象几乎不需要任何维护。应用程序只需要注意文档是否获得了新属性;如果没有新属性,就需要了解如何按照目前架构版本对其进行更新。
MongoDB 将大幅降低许可费用和基础设施成本,但组织弃用 RDBMS 实现的最高成本节省很可能来自开发成本的降低。因为需要维护的代码减少了,且应用程序更方便未参与代码写入的人员理解。MongoDB 不仅大幅降低了迁移的复杂性,且确保不会轻易受故障和停机的影响。此外,还能够以更简单的方式更频繁地更新应用程序,完全不必操心架构更新是否会失败、是否需要回滚。总体来说,利用类似 MongoDB 等 NoSQL 数据库全程维护应用程序的难度大大降低。各个环节的效率提升之后,长远下来就能够节省大量成本。
另外,值得一提的是,开发群体中的许多后起之秀将关系数据库看作过时技术,不会优先考虑该技术。MongoDB 能协助组织吸引顶尖人才,而组织必须配备人才这个关键因素才能够开发出最佳产品、加速实现价值。
提升 NoSQL 数据建模的技能
要想通过学习如何对数据建模来遏制软件开发生命周期中隐藏的成本,请选择 MongoDB University。我们提供了特殊课程 M320: MongoDB 数据建模。我们还提供大量其他免费课程、可自我调节进度的视频课、按需开放的实验室和认证,各个课程都配有数字徽章,能够协助用户全方位掌握 MongoDB 开发知识。
文章原文链接:MongoDB官网
扫码加入钉群,与MongoDB专家一对一沟通,了解更多阿里云MongoDB产品与方案,市场活动及线上培训等内容。