本文是一篇导读性质的文章,编译自:https://www.prisma.io/blog/comparison-of-database-models-1iz9u29nwn37。
数据库是软件世界里的基础。它是现实世界的投射,反应了开发者对现实世界的思考以及对其的抽象;一旦决定了数据库选型,数据库便会对软件/应用造成深远影响,它决定了开发者对数据的处理方式。
数据库自二十世纪中期以来,诞生了像网状数据库(Network Database)、层次数据库(Hierarchical Database)、关系型数据库(Relational Database)等很多种有趣的数据库模型,有些数据库模型发挥了其作用后,逐渐消失在历史的尘埃中;有些数据库模型经受住了历史地考验,如今依然站立在时代的潮头。数据库的发展并不因为其历史悠久而停滞,而是随着新的业务场景和新的挑战不断出现,促使着新的数据库模型也在不断地提出,更好地适应新的业务场景和解决新的挑战。
本文仅当作抛砖引玉,供大家参考。
历史的遗留
在软件发展的历史上,有很多盛极一时的数据库模型,或多或少有着一些致命性的缺点,慢慢地只存在于遗留系统中,或者是解决一些特定的问题。如果读者不感兴趣的话,可以直接跳到关系型数据库的那个章节。
文件型数据库(Flat-file Database)
文件型数据库可以认为是最简单的一种数据库模型,其实现非常简单,就是读写一个简单的文件,分隔符多样,文件类型可以是CSV、TSV或者是JSON文件。数据直接以人类可读的形式呈现,程序也比较容易解析。
下面的代码来源于《Designing Data-Intensive Application》一书,可以实现简单的文件型数据库读取和写入。
代码语言:javascript复制#!/bin/bash
db_set () {
echo "$1,$2" >> database
}
db_get () {
grep "^$1," database | sed -e "s/^$1,//" | tail -n 1
}
文件型数据库其实现非常简单,同样地这也限制了它的使用。例如在并发和并行的场景下,文件型数据库就表现的不是那么友好,且读取性能不是那么优秀,单点查询和范围查找的需要从头遍历整个文件。
尽管其有种种种不足,因为其简单的实现,现代操作系统依然会使用其作为内部配置文件(/etc/passwd
和/etc/fstab
),特别是 Linux 系统。
数据库实现:
- Linux 系统里的
/etc/passwd
和/etc/fstab
文件
层次数据库(Hierarchical Database)
层次数据库模型诞生于1960年代,是文件型数据的下一个形态。层次数据库模型类似于一个树状结构,每条记录有且仅有一个父节点,类似于下图。 层次数据库是人们第一次尝试去思考和处理复杂数据,在一定程度上提升了文件型数据库的一些特定数据读取方式的性能,但是层次数据库是基于树状的模型,访问层次数据库类似于遍历链表,很难实现对复杂数据的读取。
虽然层次数据库在现代系统中没有什么太大的应用价值,但是它依然使用在Linxu/Windows文件系统、DNS和LDAP系统中。
数据库实现:
- Linxu/Windows文件系统
- DNS
- LDAP
网状数据库(Network Database)
网状数据库也诞生于1960年代。数据系统语言会议(CODASYL)的委员会标准化了网状数据库模型,因此网状数据库模型也被称为 CODASYL 模型。
网状数据库进一步发展了层次数据库模型,解决了层次数据库有且仅有一个父节点的问题。网状数据库模型的子节点可以拥有多个父节点,同时这也带来了更高的复杂性。
和层次数据库模型一样,网状数据库模型的查询和更新都需要遍历链表,给开发带了无尽的难度,这使得网状数据库模型逐渐没落。毕竟能使用网状数据库模型的地方,都能找到更好的替代品。
数据库实现
- IDMS
关系型数据库(Relational databases)
关系型数据库模型诞生于1960年代,是现存的数据库模型中活得最久、生命力最旺盛、使用的最为广泛的数据模型。关系型数据库模型在刚提出来时,并不受到待见,但是在二十世纪八十年代成为了绝大多数人的首选数据库。
关系型数据库模型基于关系代数,它认为:数据可以被组织成关系(SQL中称作表),其中每个关系是元组(SQL中称作行)的无序集合。换句话说,一个关系(表)只是一个元 组(行)的集合。
关系型数据库模型解决了层次数据库模型无法表达多对多关系的能力;在关系型数据库中,读取数据时不再需要像访问链表一样去访问数据,开发者可以随意读取表中的任意行和列;并且关系型数据库引入了外键的概念,使得表和表之间可以轻易的关联起来。
适用于关系型数据库的查询语言是SQL。SQL作为一个图灵完备的语言,已经不仅仅局限于关系型数据库,而是成为数据库领域中当之无愧的王者。SQL的核心在于查询优化器,由查询优化器自动决定SQL的哪些部分以哪个顺序执行,以及使用什么索引。
总的来说,关系型数据库模型以其强大的灵活性和适应力成为了开发者的首选数据库模型。
数据库实现:
- MySQL
- PostgreSQL
- SQLite
NoSQL数据库
NoSQL诞生于二十一世纪,刚开始是为了推翻关系型数据库的统治地位,提供更强大的分布式能力,即“No SQL”,到后来逐渐发展为“Not Only SQL”,自身作为了关系型数据库的补充。
NoSQL数据库相比于关系型数据拥有以下的优势:
- 更好的可扩展性;
- 更推崇免费与开源。
键值数据库(Key-Value Database)
键值数据库诞生于二十世纪七十年代,但是到了二十一世纪初才开始广泛应用。键值数据库模型非常简单,数据存储包括键(Key)和值(Value),开发者通过指定的键可以查到特定的数据值。
值的格式可以多种多样,例如图片、文件或者是字符串。一般来说,键值数据库并不会要求数据有着严格的格式要求,其客户端也仅仅提供了有限的几种方式进行交互。
键值数据库使用起来非常简单,因此在着特定需求的业务有着非常好的作用,例如存储配置文件、缓存数据、状态信息。
但由于键值数据库并不对数据的类型和格式进行检查,因此需要开发者自己保证数据合规和数据类型正确。
数据库实现:
- Redis
- memcached
- etcd
文档数据库(Document databases)
文档数据库模型是在2009年后兴起的数据库模型。文档数据库模型与键值数据库模型类似,也有着唯一的ID作为键,但是与键值数据库模型不同的是,文档数据库模型Value是结构化的数据,例如JSON、BSON或者是XML格式。
尽管文档数据库模型的Value是结构化的数据,但是数据本身是没有严格需要提前定义的Schema的。因此文档数据库里的每一个文档都有可能有着不同的格式,是可以被进一步查询和解析的。
文档数据库弥补了关系型数据库和面向对象的编程语言的阻抗不匹配(impedance mismatch),拥有极大的架构灵活性。
因为文档数据库不需要提前定义Schema,可以减少应用程序对Join的需求,但是应用程序需要做额外的工作来保持数据的一致性:通过向数据库发出多个请求,在应用程序代码中模拟Join。但是这也将复杂性转移到应用程序中,这要比由数据库内的专用代码执行的Join更慢。
总的来说,文档数据库对于创业公司是一个很好的选择,因为开发者可以很轻易的改变数据存储的结构,而不影响现有的数据。但是要注意,文档数据库模型的灵活性对于保持数据一致性会是一个极大的挑战。
数据库实现:
- MongoDB
- RethinkDB
- Couchbase
图数据库(Graph databases)
图数据库兴起于2000年代。图数据库使用node、edge和properties的概念去描述数据。
图数据库将数据表达为一个单独的node,node拥有着任意数量的properties。在每个node之间,使用edge(也被称为relationship)关联。从某种意义上来说,图数据库将相关数据放入到node中,并且通过edge关联起这些数据。因此常用于表达数据之间的联系。
图数据库最常见的用途就是查询在社交网络中两个用户之间的联系。关系型数据库需要join多张表,而图数据库能直接查询出来。
数据库实现:
- Neo4j
- JanusGraph
- Dgraph
列簇数据库(Column-family databases)
列簇数据库兴起于2000年代,通常也被称为非关系型列式存储(non-relational column stores)、宽列(wide-Column)数据库,属于NoSQL的一种,但是从客户端看起来很像关系型数据库。
与关系型数据库一样,列簇数据库也有行和列的概念,但是这两个概念的本质是完全不同的。
在关系型数据库里,一个schema定义了一张表里的列,具体了某个列应该在哪个地方,还有相对应的数据类型等等。所有行都使用一个固定的schema。除了Table的概念以外,列簇数据库有着固定的结构,称为列簇。列簇包括了一定行的数据,每一行定义它们自己的格式。每一行数据有着唯一的列标识符,用来确定某一列的数据。
基于这个设计,列簇里的每一行都有着自己独有的schema。这个schema非常灵活,它的改变只会影响到单独的行。因此你可以把列簇数据库理解为另一种形式的键值数据库。
列簇数据库拥有优异的可扩展性,因为所有列都放在一块,因此无需大量的join操作。
但是列簇数据库不是万能的,如果你的数据需要大量的join,列簇数据库就不是一个很好的选择,同样的,类似于求和、求平均以及其它类型的分析型操作都不适合列簇数据库。
数据库实现:
- Cassandra
- Hbase
NewSQL
NewSQL诞生于2010年代。NoSQL数据库对于很多场景来说都是一个非常棒的数据库模型,特别是那种传统关系型数据不能胜任的场景。因为NoSQL诞生于当代,因此它们更关注于高性能和可用性。
从关系型数据库诞生以来都没有一种方案给它们带来可扩展性。为了解决这种需求,新型的关系型数据库诞生了,它们被称为NewSQL。
NewSQL数据库有着关系型数据库的结构和特征,但是它们更加现代,扩展性更好。它们的目标就是提供比关系型数据库更好的可扩展性,比NoSQL更好的一致性保证。为了达到这个目的,NewSQL在网络必然出现波动的情况下牺牲了一定的可用性。
为了解决可用性的问题,新的架构被发展出来以减少网络分区的影响。例如对数据进行数据切分,减少网络分区对全局数据造成影响。更深入地说,NewSQL提供了某种机制去自动切换集群地成员角色。
NewSQL提供了与传统关系型数据类似的特征,因此需要区分它们和传统关系型数据库的差异。NewSQL数据库没有传统关系型数据库那样完备的数据库特征,通常也仅仅只会提供完整SQL标准的子集。许多NewSQL数据库需要大量的内存缓存数据,提高性能的同时也承担了丢失数据的分享。
NewSQL数据库很适合那种需要关系型数据库的且有需要可扩展性的场景,未来它们会是比NoSQL更好的替代品。
数据库实现:
- MemSQL
- VoltDB
- Spanner
- Calvin
- CockroachDB
- FaunaDB
- yugabyteDB
总结
本文简单的介绍了远古时代的数据库模型层次数据库、网状数据库,现在的主流数据库模型关系型数据库、NoSQL数据库以及正在风头上的的NewSQL数据库,它们的诞生不是无缘无故的,都是应某种需求而生。每一种数据库模型都有着鲜明的特点,及其擅长的领域。
但是在计算机世界里,没有一种万能的银弹能解决所有问题。开发者需要根据自己遇到的问题和情况选择合适的数据库模型:使用存储配置信息时,关系型数据库模型就不适合了,而是要选择键值数据库模型;遇到多对多的关系时,关系型数据库模型比文档数据库模型更合适。