> * 原文地址：[How to choose the right database](https://towardsdatascience.com/how-to-choose-the-right-database-afcf95541741)
> * 原文作者：[Tzoof Avny Brosh](https://medium.com/@tzoof)
> * 译文出自：[掘金翻译计划](https://github.com/xitu/gold-miner)
> * 本文永久链接：[https://github.com/xitu/gold-miner/blob/master/TODO1/how-to-choose-the-right-database.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-to-choose-the-right-database.md)
> * 译者：[Jessica](https://github.com/cyz980908)
> * 校对者：[Ruby](https://github.com/RubyJy)，[Starry](https://github.com/Starry316)

# 如何选择合适的数据库

#### 我们将讨论现有数据库的类型以及对于不同项目类型的数据库最佳实践。

无论您是已有工作经验的软件工程师还是正在写大学课设的学生，您总会遇上要为项目选择一个数据库的情形。

如果您曾经使用过数据库，您可能会说“我只会选择 X，那是我所知道并使用过的数据库”，当然如果性能不是系统的重要要求的话，这是完全可以的。否则，当项目规模发展时，错误的数据库可能会成为项目的障碍，并且有时还很难修复。即使您正在负责一个已经使用某个特定的数据库一段时间的成熟项目，了解其局限性并清楚何时应在堆栈中添加另一种类型的数据库（多个数据组合工作是很常见的）也是很重要的。

了解不同数据库及其属性的另一个加分原因是，它是面试中的一个常考题！

在这篇文章中，我们将会讨论两种主要的数据库类型：

* 关系型数据库（基于 SQL）。
* NoSQL 数据库。

我们将讨论不同类型的 NoSQL 数据库以及何时使用它们。
最后，我们还会讨论关系型数据库与 NoSQL 数据库的优缺点。
这篇文章将不会涉及对同类型数据库的不同产品之间的比较（例如 MySQL 和 MS SQL Server）。

文章总结：如果您想要一个快速看完这篇文章的小抄，请跳到文章的最后。

---

![](https://cdn-images-1.medium.com/max/2000/1*boWbSGHBRs2Bc13aeHVDfw.png)

## 关系型数据库（基于 SQL）

关系型数据库由一组连接起来的表（比如 CSV 表）组成。表中的每一行代表一条记录。

为什么叫关系型? 在这种数据库中存在的“关系”是什么？
假设您有一个学生信息表和一张课程成绩表（课程，成绩，学生证），每个成绩行都与学生信息表的一条记录**相关**。
参见下图，课程成绩表中 “Student ID” 列的值通过 “ID” 列的值指向 “Students” 表中的行。

所有关系型数据库都使用类似 SQL 的语言进行查询，这些语言很常用，并且自带 JOIN 操作（即连接操作，用于把来自两个或多个表的行结合起来，如上文的学生信息表和课程成绩表）。
这种数据库支持对列进行索引，使得基于这些列能进行更快的查询。

由于其结构化的特性，关系型数据库的 schema（schema 指数据库中数据的组织和结构）是在插入数据之前确定好的。

**常见的关系型数据库：** MySQL、PostgreSQL、Oracle、MS SQL Server。

---

![](https://cdn-images-1.medium.com/max/2000/1*xtPrVMwIcya4ObgwTeg9GA.jpeg)

## NoSQL 数据库

虽然在关系型数据库中，所有内容都是按行和列进行结构化好的，但在 NoSQL 数据库中，并没有针对所有记录通用的结构化的 schema。大多数 NoSQL 数据库存储的是 JSON 记录，不同的记录可以包含不同的字段。

---

实际上，应将 NoSQL 数据库称为“不仅仅是 SQL（Not mainly SQL）” —— 因为许多 NoSQL 数据库支持使用 SQL 进行查询，但使用它们并不是最佳实践。

#### NoSQL 数据库主要有 4 种类型：

## 1. 文档存储数据库

文档存储数据库的原子单位是一个文档（document）。
每个文档都是一个 JSON，不同文档可以有不同的 schema，包含不同的字段。
文档存储数据库允许对文档中的某些字段建立索引，从而能够基于这些字段进行更快的查询（这将会强制所有文档都具有该字段）。

**应该什么时候选择它？**  
数据分析 —— 由于不同的记录之间并不相互依赖（在逻辑和结构方面），所以**这种数据库支持并行计算**。
我们可以借助它来轻松地对数据进行大数据分析。

**常见的文档存储数据库：** MongoDB、CouchDB、DocumentDB。

![](https://cdn-images-1.medium.com/max/2000/1*--zqXFzt3rNFLF4hvkgX7Q.jpeg)

## 2. 列存储数据库

列存储数据库的原子单位是表中的一列，这意味着数据是按列存储的。它的列存储特点使得基于列的查询非常高效，并且由于每列上的数据几乎拥有相同的结构，因此可以更好地压缩数据。

**应该什么时候选择它？**  
当您倾向于查询数据中的一个列子集时（每次查询的数据不需要都是相同的子集！）。
列存储数据库执行此类查询的速度非常快，因为它只需要读取这些特定的列（而基于行存储的数据库则必须读取整个数据）。

* 这在数据科学中很常见，其中每一列代表一个特征。作为一名数据科学家，我经常使用特征子集来训练我的模型，并且通常还会检查特征和得分之间的关系（相关性、方差、显著性）。
* 这在日志中也很常见 —— 我们通常在日志数据库中存储很多字段，但在每个查询中只使用几个字段。

**常见的列存储数据库：** Cassandra。

![列存储数据库 vs. 行存储数据库。](https://cdn-images-1.medium.com/max/2000/1*4qcFp6XOvQj3_uf4_Jx-VA.jpeg)

## 3. key-value 存储数据库

查询仅基于键 —— 您请求一个键，拿到对应的值。
不支持跨不同记录值之间的查询，比如 “select all records where city == New York”。
这种数据库中一个有用的特性是 TTL 字段（time to live），当记录将要从数据库中删除时，这个字段可以为每个记录和状态设置不同的值。

**优点 ——** 它很快。
首先是因为使用唯一键，其次是因为大多数 key-value 存储数据库将数据存储在内存（RAM）中，从而可以快速访问。
**缺点 ——** 您需要定义唯一的键，这些键是很好的标识符，是在查询时根据您所已知的数据创建的。
通常比其他类型的数据库更加昂贵（因为它是在内存上运行的）。

**应该什么时候选择它？**  
它主要用于缓存，因为它非常快，并且不需要复杂的查询，而且 TTL 特性对缓存非常有用。它还可以用于需要快速查询并满足 key-value 格式的任何其他类型的数据。


**常见的 key-value 存储数据库：** Redis、Memcached。

![](https://cdn-images-1.medium.com/max/2000/1*toVGNhjap7O02NgIAAo7PQ.jpeg)

## 4. 图存储数据库

图存储数据库包含代表实体的节点和代表实体之间关系的边。

**应该什么时候选择它？**  
当您的数据是类似于知识图谱和社交网络这种图时。

**常见的图存储数据库：** Neo4j、InfiniteGraph。

---

![](https://cdn-images-1.medium.com/max/2000/1*aoxi7WigljAnHpqyTzzLEg.png)

## 关系型数据库 vs. 文档存储数据库

您现在可能已经知道了答案了，这个问题没有标准答案，没有一个数据库能够解决所有的问题。
我们通常使用的是最常见的关系型数据库和文档存储数据库，因此我们将对它们二者进行比较。

#### 关系型数据库的优点

* 它的数据结构简单，可以匹配项目中常见的大多数类型的数据。
* 它使用 **SQL**。SQL 很常用，并且天生支持连接操作。
* 允许**数据的快速更新**。所有数据库都保存在一台机器上，记录之间的关系用作指针，这意味着您可以一次更新一条记录，而它的所有相关记录也将立即更新。
* 关系型数据库也 **支持原子事务**。
什么是原子事务：假设我想把 X 美元从 Alice 账户转移到 Bob账户。我想执行 3 个操作：减少 Alice 账户 X 刀，增加 Bob 账户 X 刀，最后记录下这个交易事务。我想把这些动作当作一个原子单位 —— 要么所有的动作发生要么一个都不发生。

#### 关系型数据库的缺点

* 由于每个查询都在表上完成 —— **查询执行**时间取决于表的大小。这是一个重要的限制，要求我们保持表相对较小，并在我们的数据库上进行优化以实现可伸缩性。
* 在关系型数据库的扩展中，可以通过向运行数据库的计算机增加更多的计算能力来进行扩展，这种方法称为“**纵向扩展**”。
为什么这是一个缺点呢？这是由于计算机能够提供的计算能力有限，而且向计算机扩展资源可能需要一些停机时间。
* 关系型数据库 **不支持 OOP**，不支持面向对象，即使表示简单的列表也是非常复杂的。

#### 文档存储数据库的优点

* 它使您可以保留具有**不同结构**的对象。
* 您可以使用可爱的 JSON 表示几乎所有的数据结构，包括**基于对象的 OOP**、列表以及字典。
* 虽然 NoSQL 本质上是无模式的（指不需要像关系型数据库一样将预定义的结构，即 schema ，向数据库说明），但它通常支持**模式验证**，这意味着您可以使一个数据集合模式化，此模式不会像表那么简单，它是一个带有特定字段的JSON schema。（译者注：这里所说的模式就是 schema）。
* NoSQL **查询**非常快，每条记录都是独立的，因此查询时间与数据库大小无关，并且**支持并行性**。
* 在 NoSQL 中，通过添加更多的机器并在它们之间分配数据来扩展数据库，这种方法称为“**水平扩展**”。这允许我们在需要时自动向数据库扩展资源，并且不会导致任何停机。

#### 文档存储数据库的缺点

* 在文档存储数据库中**更新**数据是一个**缓慢**的过程，因为数据会在不同的机器之间进行划分、复制。
* **原子事务本身不受支持**。您可以通过使用验证和恢复机制将其添加到代码中，但是由于记录是在机器之间划分的，所以它不可能是一个原子过程，并且可能会出现竞争状况。（译者注：MongoDB 4.0 版本已经提供了原生的事务操作）

---

![](https://cdn-images-1.medium.com/max/7802/1*OeNlPHG6RC2C37ycYKxyQg.png)

## 快速小抄：

* 对于**缓存** —— 使用 **key-value 存储数据库**。
* 对于**类似图形**的数据 —— 使用**图存储数据库**。
* 如果您倾向于查询**列子集**以及查询特征 —— 使用**列存储数据库**。
* 对于所有的其他用例 —— 使用**关系型数据库**或者**文档存储数据库**。

> 如果发现译文存在错误或其他需要改进的地方，欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR，也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。

---

> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区，文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域，想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。
