译术 | SQL vs NoSQL:异同比较
译者按:SQL与NoSQL之争近年来十分受到关注,既存在“SQL吃遍天下”这种理念,也不乏“NoSQL一定代表着先进生产力”这种谬误。争者往往不求甚解,为弄潮而争,忽略了本质。这篇文章原文地址:http://www.sitepoint.com/sql-vs-nosql-differences/ 作者对SQL与NoSQL在多个层面进行了细致的比较。值得一读。
SQL(结构化查询语言)数据库成为主流的数据存储机制已经存在了80余年。在1990年代末,随着web应用和诸如MySQL、PostgreSQL和SQLite这类开源项目的兴起,SQL随之得到爆发式应用。
NoSQL数据库产生于1960年代,但是最近才出现了一些类似于MongoDB、CouchDB、Redis和Apache Cassandra这样的流行选择。
你可能会看到很多探讨如何使用SQL或NoSQL的某个特性的文章。对如何从中二选一的讨论却并不多见。我希望能填补这个空白。这篇文章中,我们会覆盖到最基本的差异。而接下来会有一篇文章针对典型的场景讨论数据存储的最优选择。
大多数例子适用于流行的MySQL SQL数据库和MongoDB NoSQL数据库。其他的SQL/NoSQL数据库是类似的,但是在特性和语法上可能略有不同。
SQL与NoSQL的神圣战争
在进一步讨论之前,让我们先来修正一些普遍的谬误:
谬误: NoSQL比SQL更好/差
有些项目更适合使用SQL数据库。有些则更适合NoSQL。 还有些则能配合使用。这篇文章不是想在两者之间决出一个胜者,因为你不可能在所有的场景下都应用同样的假设。
谬误: NoSQL和MySQL之间有着清晰的鸿沟
这不一定是对的。有些SQL数据库正在加入NoSQL的特性,反之亦然。这两者的界限很可能会越来越模糊。而新的混合数据库能在未来提供有趣的选择。
谬误:编程语言/框架 决定了数据库
我们越来越习惯各种各样的技术栈:
- LAMP: linux, Apache, MySQL(SQL), PHP
- MEAN: MongoDB(NoSQL), Express, Angular, Node.js
- .NET, IIS和SQL Server
- Java, Apache和Oracle
这些技术栈产生可以归于应用的,历史上的和商业上的原因,但别被限死了。 你当然可以在你的PHP或.NET项目使用MongoDB的NoSQL数据库。你也可以在Node.js里面连接MySQL或SQL SERVER。可能你没法找到那么多教程和资源,但是你的需求本身应该决定数据库的类型,而不是编程语言。
(这意味着,别给自己故意挖坑!原则一个不常见的技术组合或SQL和NoSQL的混合式可能的,但是在寻求帮助和找到有经验的开发者方面会遇到不少困难
)
记住了这些,让我们来看看异同的比较。
SQL的表 vs NoSQL的文档
SQL数据库提供了一族相关的数据表。举例来说,如果你在运营一个线上的书店,书的信息可以被加入到一个叫book的表。
ISBN | title | author | format | price |
---|---|---|---|---|
9780992461225 | JavaScript: Novice to Ninja | Darren Jones | ebook | 29.00 |
9780994182654 | Jump Start Git | Shaumik Daityari | ebook | 29.00 |
每一行都是一条不同的书德尔记录。这种设计是比较苛刻的,你没法使用同样的表去存储不同的信息或者在是数字类型的字段插入字符串。
NoSQL数据库存储类JSON的键值对文档:
{
ISBN: 9780992461225,
title: "JavaScript: Novice to Ninja",
author: "Darren Jones",
format: "ebook",
price: 29.00
}
相似的文档可以被存储进一个集合,这跟SQL表十分类似。但是,在文档里面你可以存储任何形式的数据,NoSQL数据库并不会抱怨:
{
ISBN: 9780992461225,
title: "JavaScript: Novice to Ninja",
author: "Darren Jones",
year: 2014,
format: "ebook",
price: 29.00,
description: "Learn JavaScript from scratch!",
rating: "5/5",
review: [
{ name: "A Reader", text: "The best JavaScript book I've ever read." },
{ name: "JS Expert", text: "Recommended to novice and expert developers alike." }
]
}
SQL表创建的是严格的数据模板,所以你很难犯错。而NoSQL更加灵活,但是能够在任何地方存储数据可能会导致持续的问题。
SQL架构 vs NoSQL去架构
在一个SQL数据库中,在你确定表和字段类型这些架构之前是无法添加数据的。SQL架构还可能包括其他的信息:
- primary keys: 主键,就像ISBN一样标示了唯一的一条记录
- indexs: 索引,经常被查询的字段会被加上索引来提高检索速度
- relationships: 关系,数据字段之间的逻辑联系
- 诸如存储过程和触发器这样的机制
你的数据架构一定要在实现任何操作数据的应用逻辑之前被设计和实现出来。尽管之后再修改是可行的,但是大量的变更会非常复杂。
在NoSQL数据库中,数据可以被非常灵活的添加。并不需要事先进行字段设计和表的设计。比如说,在MongoDB中,下面的命令会在book表中创建一条新的记录,如果book表不存在,那它也会被同时创建:
db.book.insert(
ISBN: 9780994182654,
title: "Jump Start Git",
author: "Shaumik Daityari",
format: "ebook",
price: 29.00
);
(MongoDB会自动在表中添加一条唯一的_id字段,随后你也可以对索引进行定义。)
一个NoSQL数据库可能更适合初始数据形式很难确定的项目中。但这并不意味着,你可以因此而偷懒:在项目开始的时候忽视设计正确的数据表,可能会在之后引入问题。
SQL 中心化 vs NoSQL去中心化
假定我们需要往book数据库中添加出版社信息。在SQL数据库中,一个出版社会包含多个标题,我们创建了一张新的publisher表:
id | name | country | |
---|---|---|---|
SP001 | SitePoint | Australia | feedback@sitepoint.com |
我们可以在book表里面添加一个publisher_id字段,来作为publisher.id的外键。
ISBN | title | author | format | price | publisher_id |
---|---|---|---|---|---|
9780992461225 | JavaScript: Novice to Ninja | Darren Jones | ebook | 29.00 | SP001 |
9780994182654 | Jump Start Git | Shaumik Daityari | ebook | 29.00 | SP001 |
这使得数据冗余被最小化,我们不必为每本书重复出版社的信息--只需要创建外键就行了。这种技术被称为中心化,而且也确实很有益处。我们可以在不用修改book的数据的前提下更新出版社信息。
在NoSQL中我们也可以使用中心化的技术。看一下在book集合中得一条文档:
{
ISBN: 9780992461225,
title: "JavaScript: Novice to Ninja",
author: "Darren Jones",
format: "ebook",
price: 29.00,
publisher_id: "SP001"
}
-- 引用了一条在publisher集合中得一条文档:
{
id: "SP001"
name: "SitePoint",
country: "Australia",
email: "feedback@sitepoint.com"
}
然而这有的时候却并不实际。更希望的是能够把数据去中心化,对每一个book重复冗余的publisher.
{
ISBN: 9780992461225,
title: "JavaScript: Novice to Ninja",
author: "Darren Jones",
format: "ebook",
price: 29.00,
publisher:
{
name: "SitePoint",
country: "Australia",
email: "feedback@sitepoint.com"
}
}
这会使我们查询的更快,但是再多条记录中更新publisher信息,会非常的慢。
SQL关系型的JOIN vs NoSQL
SQL查询提供了强劲的JOIN语法。我们可以使用一条SQL语句在多个数据表中获取关系数据库。举例来说:
SELECT book.title, book.author, publisher.nameFROM bookLEFT JOIN book.publisher_id ON publisher.id;
这个语句会返回所有的书的标题,作者以及相关的出版人姓名。(假设出版人姓名存在)
NoSQL没有相对应的JOIN,这可能对那些熟练使用SQL的人非常不习惯。如果我们使用中心化的NoSQL集合,那我们需要拉取所有的book文档,再获取所有的publisher文档,再手动的通过程序逻辑来把两者联系起来。这也是为什么对NoSQL往往使用去中心化的方式很有必要。
SQL vs NoSQL 数据完整性
大部分的SQL数据库允许你通过外键限制的方式来强制的保证数据完整性。(除非你还在使用MySQL中得陈旧 不再被维护的MyISAM引擎)我们的book数据表能够:
- 保证所有的book数据能够有一个合法的对应在pubilisher表中得publisher_id
- 如果有book数据引用了publisher信息,那么这条数据不能被删除
这种模式强制了数据库应该遵循的规范。对于开发者或者用户而言,无法在可能引入孤儿数据或非法数据的情况下,对数据条目进行编辑或删除。但是在NoSQL中却没有类似的数据完整性保证。你可以不管其他的文档,只存储你想要存储的内容。理想的情况下,一个数据条目应该成为关于一个事物的唯一信息来源。
SQL vs NoSQL 事务
在SQL数据库中,两条或多条更新语句能够在一个事务(保证成功或失败回滚的机制)中被同时执行。举例来说,假设book数据库包含订单和库存两张表。当一本书被订购的时候,我们需要往订单表添加一条数据,然后在库存表中将库存字段减一。如果我们把这两条更新语句独立执行,一条可能失败,另一条可能成功。因此会造成数据的不同步。而把他们通过事务的方式执行,就能保证一起成功或一起失败。
在NoSQL数据库中,对单个文档的修改是原子的。也就是说,如果你在文档中更新三个字段,那么要么三个字段同时更新,要么都不变。但是对于多条文档的更新而言却没有事务。不过有一个类事务的选项(http://docs.mongodb.org/manual/core/write-operations-atomicity/)。不过在写这篇文章的时候,这些还都需要你在代码里自行处理。
SQL vs NoSQL CRUD 语法
创建、读取、更新和删除数据是所有数据库系统的基础。本质上来说:
- SQL是轻量级的解释性语言。语法强大,并且已经成为了国际标准。尽管大多数系统实现语法的时候略有不同。
- NoSQL数据库使用带json参数的类javascript语言一样的查询。基本的操作比较简单,但是对于更复杂的查询来说,嵌套的JSON会非常的繁复。
一个快速的对比:
SQL | NoSQL |
---|---|
SP001 | SitePoint |
插入一条book记录 | |
INSERT INTO book ( `ISBN`, `title`, `author`)VALUES ( '9780992461256', 'Full Stack JavaScript', 'Colin Ihrig & Adam Bretz'); |
db.book.insert({ ISBN: "9780992461256", title: "Full Stack JavaScript", author: "Colin Ihrig & Adam Bretz"}); |
更新一条book记录 | |
UPDATE bookSET price = 19.99WHERE ISBN = '9780992461256' |
db.book.update( { ISBN: '9780992461256' }, { $set: { price: 19.99 } }); |
返回所有$10以上的书的标题 | |
SELECT title FROM bookWHERE price > 10; |
db.book.find( { price: { >: 10 } }, { _id: 0, title: 1 }); 第二个JSON对象就是所谓的projection: 它设置了哪些字段要被返回 (_id字段是被默认返回的,所以需要覆盖它). |
计算所有SitePoint网站的书的数量 | |
SELECT COUNT(1) FROM bookWHERE publisher_id = 'SP001'; |
db.book.count({ "publisher.name": "SitePoint"}); 这条语句假定使用了NoSQL的去中心化设计 |
返回book的格式类型的数量 | |
SELECT format, COUNT(1) AS `total`FROM bookGROUP BY format; |
db.book.aggregate([ { $group: { _id: "$format", total: { $sum: 1 } } }]); 这就是所谓的聚合:一个新的文档集合从原始的文档集合计算出来。 |
删除所有的SitePoint书 | |
DELETE FROM bookWHERE publisher_id = 'SP001'; |
db.book.remove({ "publisher.name": "SitePoint"}); |
SQL vs NoSQL 性能表现
或许这是最有争议性的比较。NoSQL通常被认为势必SQL更快的。这并不奇怪。NoSQL更简单的去中心化存储允许你在单词请求中获取一个条目的所有信息。因此并不需要相关的JOIN或复杂的SQL查询。
也就是说,你的项目设计和数据库设计的影响很大。一个被设计的很好的SQL数据库肯定比设计的很差的NoSQL数据库性能好很多,当然反之亦然。
SQL vs NoSQL 扩容
随着你数据的增加,你可能会觉得有必要把负载分布到多台服务器。对于基于SQL的系统来说,这有时候没那么容易。你如何分配相关的数据呢?集群化可能是最简单的选项;多个服务器访问相同的中心化存储 -- 但是即使这样也会有挑战。
NoSQL简单地数据模型会使得扩容简单一些,很多NoSQL数据库一开始就自建了扩容的功能。不过当你遇到实际问题的时候,还是最好寻求专家的建议。
SQL vs NoSQL 实用性
最终,来考虑下安全和系统的问题。最流行的NoSQL数据库已经有几年了。不过它们相比于成熟的SQL产品,问题还是较多。不过大多数被报告的问题,还是因为一个原因:知识不足.
开发者和系统管理者对新的数据库系统经验不足,因此不免会犯错误。如果因为NoSQL比较新鲜或者因为你想避免最终必会发生的范式设计而使用它,那么之后你很可能会遇到问题。
SQL vs NoSQL 总结
SQL和NoSQL数据库用不同的方式做着同样的事情。在刚开始选择一种,之后在进行切换时完全可行的,但是预先设计肯定会节省时间和金钱。
适用于SQL的项目:
- 可以被预先确定的逻辑相关的离散数据
- 数据完整性是必须的
- 需要具有丰富开发者经验和支持的标准技术的项目
适用于NoSQL的项目:
- 非关系型的、模糊的或是不断演进的数据存储需求
- 简单、宽松的项目目标,能够快速的开始编程
- 速度和可扩展性很有必要
在我们的书店的例子中,一个SQL数据库看起来是更加实际的选择。尤其是当我们遇到要求严格的事务支持的电子商务场景的时候。在下一篇文章中,我们会讨论更多的项目场景,以及决定到底SQL或是NoSQL数据库是个更好的选择。