分享到

简介

数据库从不孤立存在。用户、应用程序、脚本,甚至其他数据库都会向其输入信息并从中提取信息,就像生物消耗和分配环境(以及彼此)的能量一样。虽然没有哪个数据库能够与地球的全球生态系统在规模或复杂性上相媲美,但设计和支持信息系统仍然与照料小型生态系统(如花园、温室或动物饲养箱)有许多相似之处。

经典的生态划分也可应用于数据库在信息系统中的角色:动物、植物还是矿物?确定哪种模式适合给定的数据模型,需要尽可能地了解其所处的环境。

Animal, vegetable, and mineral modes

“动物”似乎描述了一种积极的组织原则,它嵌入(或者说,纠缠)在操作流程和工作流中,经过适应和编程——简而言之,就是专业化。以“动物”方法构建的系统或子系统,若明确围绕单一数据库作为信息流的收集器和单一真相来源,则表现出色。如果失去这种专注,“动物”型数据库就有可能变成一只大熊猫:行动迟缓、维护成本高昂,并带来物流和官僚噩梦。

更“植物”型的数据库只组织其获取的数据,不多不少,它约束和提炼信息,但不采取积极行动。这类数据库用适应性换取了对工作流的集中控制和积极管理,它们收集和组织信息,但不将行为和使用假设编码进更严格的数据库结构中。在数据库并非唯一信息处理场所,或有更大团队和复杂沟通结构交互的系统中,处理一个较少编程的数据库会更容易,因为它被视为 一种 而非 唯一的 组织原则。

还有一些“矿物”型数据库设计甚至放弃了对信息的最低限度管理权限,仅提供最少结构的存储,就像五千年前苏美尔人刻写账簿和投诉的泥板一样。有时确实只需要一个符合 ACID 原则的电子表格,但更仔细地审视系统是非常有必要的。此外,现有“矿物”型数据库的实现可能未能考虑到或笨拙地近似于更复杂的数据模型,或者数据模型本身的一部分或全部不适合关系型表示。

多个数据库

每个与数据库交互的程序或系统——包括设计者、用户或开发者——也都扮演着生态角色,它们消耗和生成信息,进行组合、分离,或增添、消除细微差别,无论是好是坏。许多这些交互系统都有自己的数据模型,其中目标系统数据模型的一部分或全部现在构成了一个组件(有时通过众所周知的模糊玻璃,正如任何曾诅咒过意外的VARCHAR(20)的人都能证明的那样)。

这些连接意味着数据库设计必须经常适应其他外部数据模型的至少一部分假设,即便不是其结构元素。信息被拆分:用户和预订在一个数据库中,酒店空房信息来自另一个数据库,航班预订信息存储在航空公司,并在用户和预订端重复。模式变得纠缠不清,一个数据库的更改会影响到其他数据库。

管理更多事物及它们之间的更多交互总是更困难的,而将数据模型拆分到多个存储尤其会产生深远的影响。对完全关系模型的子部分进行封装和处理,通过模式可以更安全地实现,所有主要的 RDBMS 都支持模式,除了 MySQL。但情况总在变化。有些信息更适合非关系型结构。有些信息已经分散在属于其他系统的数据库中,或者面向服务架构(宏观或微观)的采用使得分裂不可避免。更严格的弹性要求需要网络化、冗余存储。现在的数据模型比以往任何时候都更需要考虑到分布在多个相互作用的数据存储中。

拆分数据模型最重要的牺牲是引用完整性。在单个数据库中,服务器可以防止违反外键约束的操作,例如插入或更新具有不存在父 ID 的记录,或者删除其他记录所依赖的记录。将数据模型的一部分移到另一个存储中,维护正确性就成了你(和你的团队)的问题。

专业解决方案

像其他任何事物一样,引用完整性是一个可协商的约束,如果能获得足够的回报,牺牲它也是一种选择。有时甚至都不是需要考虑的问题,如果数据模型在图论意义上足够简单,实体很少,实体之间连接很少或主要是层次结构,或两者兼而有之。性能和可扩展性要求也可以成为引入关系模型替代方案的理由,在这些情况下,数据模型的一部分以比 RDBMS 轻松容纳的更高容量和更快流动的形式表示实际信息。

21 世纪 00 年代后期,这些替代方案出现了寒武纪大爆发。突然之间,出现了存储文档、图、键值对的“NoSQL”数据库。列式存储通过将表横向化并建模扁平、非规范化数据,实现了惊人的性能。新来者拥抱了跨网络服务器的横向扩展,抛弃了 ACID 规则,转而青睐 BASE(“基本可用、软状态、最终一致性”),并开发了与 SQL 概念迥异的 API 和领域特定语言。不仅如此,围绕数据存储的讨论重塑了数据库作为概念的边界。像 Kafka 这样的流处理器是数据库吗?像 ElasticSearch 这样的搜索引擎呢?它们每个都从根本上促进了信息的存储和检索;讨论其中任何一个都需要调用大量的数据库技术语言。更根本的是,它们每个都是特定类型数据建模问题的解决方案。

如果你需要存储和处理...请考虑...
连接很少的层级结构、原始结构化文档或至少有一些共同字段的对象文档数据库 (Couchbase, MongoDB)
大量“扁平”数据,例如仪表读数或结构化分析数据面向列的数据库 (Cassandra, HBase),时序数据库 (TimescaleDB, InfluxDB, Druid)
仅通过键标识和查询的记录键值存储 (Redis, LevelDB),缓存 (memcached)
记录之间具有点对点导航的复杂关系图数据库 (OrientDB, Neo4j, TerminusDB)
按接收顺序的瞬态数据流流处理器和队列 (Kafka, RabbitMQ, ZeroMQ)
文件。大量的文件云文件存储 (Google Cloud Storage, Amazon S3 and Athena)
非结构化文档,或模式高度可变的结构化文档搜索引擎 (ElasticSearch, Solr),内容存储库 (Jackrabbit)
行星级规模的关系数据NewSQL 高度分布式关系数据库 (VoltDB, Spanner)
以上至少几种多模型数据库 (FaunaDB, ArangoDB)

请注意,以上仅为示例,并非推荐!Kristof Kovacs 也维护着一份更详细的总结,涵盖了许多 NoSQL 选项,从流行到冷门。

默认选项

如果你不需要存储至少数千兆字节的、偏离关系数据模型的例外情况,你甚至可能不需要放弃 ACID 和引用完整性!关系型数据库通常在全文搜索和存储结构化或非结构化文档方面表现出色;特别是 PostgreSQL 对 JSON 的处理非常广泛,具有可索引的二进制存储和丰富的查询工具集,使得混合关系-文档策略变得实用。Postgres 和其他 RDBMS 都通过服务器组件、扩展或存储引擎支持其他模型,其中一些专业存储,如 TimescaleDB,是建立在关系型数据库之上的。

因此,尽管几十个营销部门不遗余力,古老的半世纪历史的 RDBMS 仍然是首选的通用数据存储,而非关系型解决方案则找到了适合其优势的专业利基市场。NoSQL 数据库可以完成其关系型对应物无法轻松或根本无法完成的各种任务,但关系型数据库在可预见的未来仍将存在。通过服务器调优、索引和表分区,单个关系型数据库可以适应除最重负载之外的所有工作。即使这样还不够,还有复制功能。

复制与架构

深入探讨复制技术超出了数据建模指南的范围,但它们的使用确实会带来不可忽视的后果。复制的数据库不仅仅是变得更大:一旦数据存储网络化,它就不再是整个系统架构中的单一元素。复制引入了一整套新的关注点,包括延迟、网络分区、维护等。

最常见的复制形式是仅将数据写入单个服务器。主服务器将更改转发给一个或多个副本服务器,这些副本服务器要么在主服务器宕机时待命替换它(故障转移),要么承担响应读取查询的工作。在后一种情况下,网络延迟可能意味着更改不会立即可见,但由于它们只单向流动,因此仍能保证一致性。这种读写分离自然而然地适用于在适当的情况下在视图中表示合并实体,特别是在软件系统中,适用于命令查询职责分离,这是一种有助于控制仅与检索或存储相关的复杂性的设计模式。

在另一种“多主”复制模式中,数据可以写入和读取多个服务器,这些服务器通过网络连接,在重负载下集体比单个服务器更具弹性。然而,达成共识是复杂的,并且要实现更高水平的写入验证以保证持久性可能会很慢。这往往需要更简单的数据模型和更精细地处理冲突和回滚。

大多数 NoSQL 数据存储都是为集群设计的,有些甚至到了在单台计算机上运行几乎是事后才考虑的地步。特别是,NoSQL 将分片(即从集群中不同节点提供表分区)的概念推向了前沿。分片是这些数据库实现规模的主要因素,但如果没有复制,丢失一个节点仍然意味着丢失数据。大多数 NoSQL 数据库提供基本复制,通常带有自动故障转移。少数数据库,如 Cassandra 和 Couchbase,使用多个主节点。

CAP 定理

他的“CAP 定理”(一致性、可用性、分区容忍性:三选二)至今仍有共鸣,但就像动物/植物/矿物三元论一样,它是一种优雅的过度简化——不多不少。

Martin Kleppmann 在 2015 年详细描述了 CAP 的缺点,这建立在 Coda Hale 早期澄清“三选二”方面的努力之上。Brewer 本人后来宣布 Google 的 Spanner 数据库“有效地达到 CA”,凭借 Google 网络的巨大规模克服了 AP/CP 的二分法。

整合外部信息

信息很少仅在系统的数据库和用户之间流通。大多数信息系统都是开放系统,数据从其他信息系统流入,也流出到其他信息系统。这些流通常适合自动化,并且在达到一定(并非非常大)的量时几乎是强制性的;手动数据输入是最后的手段。有时,外部信息源是持续在线的,队列、流处理器或专用守护程序会在记录到达时添加它们,但从不同时处理太多。然而,其他时候,实时数据可能不可用,或者根本不必要——月销售额或昨天的网站分析不需要精确到秒。

批量数据摄取过程通常被称为 ETL,即提取-转换-加载 (Extract-Transform-Load) 的缩写,即使在没有提取或转换发生的情况下,也非正式地如此称呼。典型的 ETL 过程是一个 cron 作业或计划任务,它从外部系统下载存档的数据导出,通常是像 CSV 或 TSV 这样的扁平分隔格式。记录可以在 ETL 任务将其写入数据库时进行转换,或者,违反缩写,之后进行批量转换。批量转换在涉及与现有数据连接时特别有用,因为数据库可以一次性优化数千行的连接,比优化数千次单行连接更有效得多。

Integrating external information

数据导入的第三方工具各不相同。专有 RDBMS 通常包含专门的 ETL 管理程序,例如 SQL Server Integration Services 或 Oracle Data Integrator,而主要的开源和 NoSQL 选项通常仅限于使用 mysqlimportmongoimport 等应用程序进行基本文件加载,或在 PostgreSQL 和 Cassandra 中使用服务器端 COPY 命令(Postgres 还在 psql 客户端中提供客户端 \copy 指令)。

然而,第三方 ETL 自动化正在成为一个可行的商业利基市场,尤其是在越来越多的数据库托管在云端的情况下。如果你有大量不同的传入数据流或严格的合规性约束,那么调查这些服务是值得的。它们做的任何事情你自己都可以用现有工具完成——问题在于你是否有时间自行实施和维护足够健壮的 ETL 过程。

联邦:统一信息源

ETL 并非唯一的选择。2003 年,SQL 标准引入了一个名为 SQL/MED 的扩展,意为外部数据管理。文件、其他数据库、其他关系型或非关系型 DBMS、REST 端点、目录服务——所有这些以及更多都涉及看起来很像列和行的数据集,至少从正确的角度看是如此。任何看起来足够像列和行的数据,都可以通过 SQL 进行查询,如果不是完全管理的话。数据库向用户和其他外部系统呈现单一界面,同时将请求和更改路由到适当的目标。

在 IBM 的 DB2 首次实现后,其他数据库用了数年时间才赶上 SQL/MED。目前,Oracle 支持将平面文件作为“外部表”进行读写访问,而 SQL Server 可以连接到“链接服务器”,只要它们符合 Microsoft 的 OLEDB API。

真正有趣的事情正在开源 RDBMS 中发生。PostgreSQL 在 9.1 版本中引入了外部数据包装器,并持续扩展其功能,而 MariaDB 的CONNECT 存储引擎可以包装大量数据源。此外,Postgres 和 MariaDB 的实现都是可扩展的:如果你有现有包装器无法处理的外部数据格式,你可以编写自己的。Postgres 社区甚至开发了一个名为Multicorn 的 Python 框架,以简化原本基于 C 语言的外部数据包装器开发过程。

“联邦”架构的理念源于区域政府和中央(联邦)政府并存的双重政府形式,与其他系统设计技术一样,其目的是管理复杂性。各个子系统的用户在其内部和与之协同工作;而那些只关心整体运作的人则与整体交互。就生态角色而言,这非常类似于数据库操作的“动物”模式。联邦数据库是其所处系统的核心。它将信息流吸引到自身并流经自身,建立连接,编码对辅助模式的期望。但在这种“巨兽”能够茁壮成长的信息生态系统中,SQL/MED 是整合不同数据源的强大工具。

关于作者
Dian Fay

Dian Fay

Dian 最初并没有计划辍学专攻 SQL 和后端开发,但事情就这样发生了。十五年后,她设计了支持从工业物流和追溯系统到拥有数百万用户的社交媒体游戏等各种数据库。她是 MassiveJS 的当前维护者,这是一个专注于充分利用 PostgreSQL 的 Node.js 开源数据映射器。
© . All rights reserved.