分享至

简介

数据库从来都不是孤单的。用户、应用程序、脚本,甚至其他数据库都将信息输入数据库并从中提取信息,就像生物消耗和分配环境(以及彼此)的能量一样。没有一个数据库可以与地球的全球生态系统相媲美,无论是在规模上还是复杂程度上,但设计和支持信息系统仍然与照料较小的生态系统(如花园、温室或人工生态系统)有着许多相似之处。

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

Animal, vegetable, and mineral modes

"动物"似乎描述了一种积极的组织原则,它嵌入(或者更不客气地说,纠缠)在运营流程和工作流程中,被改编、编程——总之,专业化。在围绕单个数据库作为信息流的收集器和唯一事实来源而明确构建的紧密聚焦的系统或子系统中,"动物"方法非常有效。失去这种关注,"动物"数据库就有可能变成一只大熊猫:行动迟缓、维护成本高,而且成为后勤和官僚主义的噩梦。

更"植物"类型的数据库会组织它所获得的数据,仅此而已,它会限制和优化信息,而不会采取积极的行动。这些数据库以适应性换取集中化和对工作流程的主动控制,收集和组织信息,而不会在更严格的数据库结构中编码行为和使用假设。在系统中,数据库不是信息处理的唯一场所,或者更大的团队和沟通结构相互作用,使用被视为一个而不是唯一的组织原则的更少程序化的数据库要容易得多。

还有一些其他"矿物"数据库设计,甚至放弃了对信息的这种最低限度的控制权,只提供存储,并具有最少量的结构,就像苏美尔人在五千年前用来记录从账簿到投诉的所有内容的粘土板一样。确实,有些情况下只需要一个符合 ACID 的电子表格,但仔细检查系统是值得的。还可能出现这种情况,现有的"矿物"数据库的实现未能考虑或笨拙地近似于更复杂的数据模型,或者部分或全部数据模型本身不适合关系型表示。

数据库(复数)

每个与数据库交互的程序或系统——不排除设计人员、用户或开发人员——也扮演着生态角色,消耗和生成信息,组合信息,分离信息,为好或为坏增加或消除细微差别。许多这些交互系统都有自己的数据模型,目标系统的部分或全部数据模型现在构成其中的一个组件(有时是通过比喻的黑暗玻璃,就像任何曾经诅咒过意外的 VARCHAR(20) 的人可以证明的那样)。

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

管理更多事物以及它们之间的更多交互总是更加困难,将数据模型拆分到多个存储库中尤其具有深远的影响。使用模式可以更安全地封装和解决完全关系模型的子部分,所有主要 RDBMS 都支持除了 MySQL。但是事情发生了。一些信息更适合非关系型结构。一些信息已经分布在属于其他系统的数据库中,或者采用面向服务的架构(宏观或微观)使分裂成为必然。更严格的弹性要求需要联网的冗余存储。数据模型必须比以往任何时候都更加考虑分布在多个交互式数据存储中。

拆分数据模型最重要的牺牲品是参照完整性。在单个数据库中,服务器可以阻止违反外键约束的操作,例如插入或更新没有父 ID 的记录或删除其他记录依赖的记录。将部分数据模型移动到另一个存储库中,维护正确性现在成为你的(以及你的团队的)问题。

专业解决方案

就像其他任何事物一样,参照完整性是一个可协商的约束条件,如果你获得足够的回报,牺牲它是可以接受的。有时,如果数据模型在图论意义上足够简单,实体很少,它们之间的连接很少或主要是层次化的,或者两者兼而有之,这个问题甚至都没有被提出。性能和可扩展性要求也可以为关系模型引入替代方案,其中数据模型的一部分以比 RDBMS 中更轻松地容纳的更高的体积和更快的变化速度来表示真实信息。

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 和 Athena)
非结构化文档或结构化文档,其模式高度可变搜索引擎(ElasticSearch、Solr)、内容存储库(Jackrabbit)
关系型数据,以行星级规模NewSQL 高度分布式关系型数据库(VoltDB、Spanner)
至少上述几种多模型数据库(FaunaDB、ArangoDB)

请注意,以上只是一些示例,并非推荐!Kristof Kovacs 还维护着一个更详细的 NoSQL 选项摘要,涵盖了从流行到冷门的各种选项。

默认值

如果您不需要存储至少数 GB 的关系型数据模型的异常,那么您甚至可能不需要放弃 ACID 和引用完整性!一般而言,关系型数据库在全文搜索和存储结构化或非结构化文档方面非常强大;特别是 PostgreSQL 对 JSON 的处理方式非常广泛,具有可索引的二进制存储和丰富的查询工具集,使混合关系型-文档策略成为现实。Postgres 和其他 RDBMS 都通过服务器组件、扩展或存储引擎支持其他模型,一些专门的存储,如 TimescaleDB,是建立在关系型数据库之上的。

因此,尽管数十个营销部门竭尽全力,但古老的、拥有半个世纪历史的 RDBMS 仍然是首选通用数据存储,非关系型解决方案找到了适合其优势的专业利基市场。NoSQL 数据库可以做很多关系型数据库做不到的事情,或者做起来很困难,但关系型数据库在可预见的未来将继续存在。通过服务器调整、索引和表分区,单个关系型数据库可以容纳除最繁重的负载之外的所有负载。即使这样都不够,仍然有复制。

复制和架构

深入介绍复制技术超出了数据建模指南的范围,但其使用确实会导致一些不可忽视的后果。复制的数据库不仅仅是更大:一旦数据存储变成联网的,它就不再是整个系统架构的单个元素。复制引入了一系列关于延迟、网络分区、维护等等的新问题。

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

在另一种“多主”复制模式下,可以将数据写入和读取多个服务器,这些服务器通过网络连接,在繁重的工作负载下比单个服务器的恢复能力强得多。但是,达成共识很复杂,并且实现更高的写入验证级别以保证持久性可能会很慢。这往往需要更简单的数据模型和更精细的冲突处理和回滚。

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

CAP 定理

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

Martin Kleppmann在 2015 年详细描述了 CAP 的缺点,该描述基于 Coda Hale 早期为澄清“二选一”方面而做出的努力。布鲁尔本人后来宣称谷歌的 Spanner 数据库“实际上是 CA”,得益于谷歌网络的庞大规模,克服了 AP/CP 二分法。

整合外部信息

信息很少仅在系统的数据库或数据库与其用户之间流通。大多数信息系统是开放系统,数据从其他信息系统流入,并流出到其他信息系统。这些流通常适合自动化,并且在一定规模(不是很大)下,几乎是强制性的;手动数据输入是最后的最后手段。有时,外部信息源始终可用,队列、流处理器或专门的守护进程在记录到达时添加记录,但不会一次做太多。但是,其他时候,实时数据可能不可用或根本没有必要——每月的销售额或昨天的网站分析不需要实时更新。

批量数据摄取过程通常被称为 ETL 首字母缩略词,代表提取-转换-加载,即使在没有提取或转换的情况下,在非正式情况下也是如此。典型的 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 可以连接到“链接服务器”,只要它们符合微软的 OLEDB API 即可。

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

“联邦”架构的概念源于区域政府和中央(联邦)政府共存的双重政府形式。与其他系统设计技术一样,其目的是管理复杂性。各个子系统的用户在这些子系统中工作并与之交互;那些只关心整体运行的人则与整体交互。就生态角色而言,这非常类似于数据库操作的“动物”模式。联邦数据库是其所处系统的核心。它从自身获取信息流并将其传递出去,建立连接,对子模式进行编码期望。但在信息生态系统中,如果这种“野兽”能够蓬勃发展,那么 SQL/MED 是管理不同数据源的有力工具。

关于作者
Dian Fay

Dian Fay

Dian 本来并没有计划退学专门研究 SQL 和后端开发,但这就是它发生的方式。十五年后,她设计了数据库,支持从工业物流和可追溯性系统到拥有数百万用户的社交媒体游戏等各种应用。她是 MassiveJS 的当前维护者,这是一个面向 Node.js 的开源数据映射器,专注于充分利用 PostgreSQL。