关系型数据库与NoSQL的对比
SQL(结构化的查询语言)数据库是过去四十年间存储数据的主要方式。20世纪90年代末随着Web应用和MySQL、PostgreSQL和SQLite等开源数据库的兴起,用户爆炸式的增长。
NoSQL数据库自从20世纪60年代就已经存在了,直到MongoDB, CouchDB, Redis 和 Apache Cassandra等数据库的流行才获取了更多的关注。
你可以很容易地找到许多关于如何使用一款特定的SQL或NoSQL的教程,但是很少有讨论你为什么优先的使用一款而不适用另一款。我希望我能够填补这个空白。在这篇文章中将会介绍它们之间的不同。在后续的文章中,我们将会根据典型的场景来确定最佳的选择。
大多数的例子都适用于流行的关系型数据库MySQL和MongoDB NoSQL数据库.其它的SQL/NoSQL也是类似的,但是在语法和特点上会有一些细微的差别。
SQL与NoSQL之间的战争
在我们进一步讨论之前,我们先不去考虑各种观点......
观点一:NoSQL将会取代SQL
这个观点就像是说船将会取代汽车,因为船是一种新的技术一样,这是不可能发生的事情。SQL和NoSQL有着相同的目标:存储数据。它们存储数据的方式不同,这可能会影响到你开发的项目,一种会简化你的开发,一种会阻碍你的开发。尽管目前NoSQL数据库非常的火爆,但是NoSQL是不能取代SQL的--它仅仅是SQL的一种替代品。
观点二:NoSQL要比SQL好/坏
一些项目可能会更适合使用SQL数据库,然而一些项目可能会比较适合使用NoSQL,有些项目使用哪一种都可以很好地达到预期的效果。本文不支持任何一方,因为没有一种方式可以使用到所有的项目中去。
观点三:SQL与NoSQL之间有明显的差别
这个观点并不是很正确。一些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。也可以在Node.js中使用MySQL或者SQL Service。或许在你使用上诉开发模式下不能找到很好的教程和资源,但是我们开发应该是需求决定使用数据库的类型,而不是数据库语言来决定的。
(换句话说,不要自讨苦吃!选择一种小众的技术组合或者是将SQL和NoSQL进行组合开发是有可能的,但是那样你会发现寻找有经验的开发者和相关的技术支持是非常困难的)
下面我们来看一下它们之间的主要的差别......
SQL中的表与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数据库采用类JOSN的键值对来存储文档,例如:
{ ISBN: 9780992461225, title: "JavaScript: Novice to Ninja", author: "Darren Jones", format: "ebook", price: 29.00 }
同一类型的文档存储为一个集合(collection),类似于关系型数据库中的表结构。然而,你可以在任意的文档中存储任意的数据,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的无模式
在关系型数据库中,除非你事先定义了表和字段的模式否则你无法向其中添加数据。模式中包含了许多的信息:
- 主键 — 独一无二的标志就像ISBN唯一确定一条记录
- 索引 — 通常设置索引字段加快搜索的速度
- 关系 — 字段之间的逻辑连接
- 设计功能例如触发器和存储程序
在进行数据的逻辑操作之前我们必须要定义数据模式。数据模式可以在后期进行更改,但是对于模式的大改将会是非常复杂的。
在NoSQL的数据库中,数据在任何时候都可以进行添加。不需要事先去定义文档和集合。例如在MongoDB中如下的操作将会在book
集合中重新创建一个文档如果之前没有创建。
db.book.insert( ISBN: 9780994182654, title: "Jump Start Git", author: "Shaumik Daityari", format: "ebook", price: 29.00 );
(MongoDB会在集合中为每一个文档添加一个独一无二的_id。如果你仍然想要定义索引,你也可以自己在之后定义)
一个NoSQL数据库更适合于那些不能够确定数据需求的的工程项目。也就是说,不要为自己的懒惰而制造麻烦:不在项目开始的时候设计一个好的数据存储模型在将来会带来一定的麻烦。
SQL语言的规范化 VS NoSQL语言的非规范化
假设我们想要在书店的数据库中添加一项出版社信息。一个出版社会出版很多书,因此在数据库中我们创建了一个表publisher
:
id | name | country | |
---|---|---|---|
SP001 | SitePoint | Australia | feedback@sitepoint.com |
我们要在book
表中添加一个publisher_id
的字段,用于引用出版社信息中的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" }
然而,在实际中我们并不会这样做。我们会更倾向于选择非规范化我们的文档为每一本书中都重复出版社的信息
{ ISBN: 9780992461225, title: "JavaScript: Novice to Ninja", author: "Darren Jones", format: "ebook", price: 29.00, publisher: { name: "SitePoint", country: "Australia", email: "feedback@sitepoint.com" } }
这样会使查询更快,但是在更新出版社信息的记录变多时效率将会显著地下降。
SQL关系的JOIN操作 VS NoSQL
SQL语言为查询提供了强大的JOIN操作。我们可以使用单个SQL语句在多个表中获取相关数据。例如:
SELECT book.title, book.author, publisher.name FROM book LEFT JOIN book.publisher_id ON publisher.id;
这条SQL语句会返回所有书的书名,作者和相关的出版社的名称。
在NoSQL中没有与JOIN相同的操作,对于具有SQL语言经验的人来说是非常令人震惊的。如果我们使用是上面的规范化的集合,我们需要取出book
集合中所有的文档,检索所有的publisher
的文档,并在程序中进行手动连接。这也是非规范化存在的原因之一。
SQL VS NoSQL 数据完整性
大多数的数据库允许通过定义外键来进行数据库的完整性约束。我们的数据库能够保证:
- 确保所有的书的
publisher_id
都会对应于publisher
中的一个实体, - 如果有一本或多本书中的
publisher_id
与publisher
中的id对应,那么该出版社就不能被删除。
数据模式确保了这些规则被数据库遵守。开发者或者用户不能添加、修改和移除一条记录,如果这些操作导致数据产生无效的数据或者单条无用记录。
在NoSQL数据库中没有数据完整性的约束选项。你可以存储任何你想要存储的数据。理想情况下,单个文档将是项目的所有信息的唯一来源。
SQL VS NoSQL 事务
在SQL数据库中,两条或者多条更新操作可以结合成一个事务(或者全部执行成功否则失败)执行。例如,假设我们的book数据库中包含了order
和stock
表。当一本书被订购之后,我们要在order
中添加一条记录并减少stock
中的库存数目。如果我们将两条更新操作分别执行,一条成功另一个失败---这将会导致数据库的不一致性。将两条更新操作绑定为一个事务确保了它们要么全部成功要么全部失败。
在NoSQL数据库中,对于一个文档的更新操作是原子性的。换句话说,如果你要更新一个文档中的三个值,要么三个值都更新成功要么它们保持不变。然而,对于操作多个文档时没有雨事务相对应的操作。在MongoDB中有一个操作是transaction-like options,但是,需要我们手动的加入到代码中。
SQL VS NoSQL CRUD(增删改查)语法
增删改查是数据库的基本操作。本质上:
- SQL是一种声明性语言。SQL语言的功能强大,并且已经成为了一种国际的通用标准,尽管大多数系统在语法上有一些细微的差别。
- NoSQL数据库使用类似JOSN为参数的JavaScript来进行查询!基本操作是相同的,但是嵌套的JOSN将会产生复杂的查询。
比较:
SQL | NoSQL |
---|---|
insert a new book record | |
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"}); |
update a book record | |
UPDATE bookSET price = 19.99WHERE ISBN = '9780992461256' |
db.book.update( { ISBN: '9780992461256' }, { $set: { price: 19.99 } }); |
return all book titles over $10 | |
SELECT title FROM bookWHERE price > 10; |
db.book.find( { price: { >: 10 } }, { _id: 0, title: 1 }); The second JSON object is known as a projection: it sets which fields are returned (_id is returned by default so it needs to be unset). |
count the number of SitePoint books | |
SELECT COUNT(1) FROM bookWHERE publisher_id = 'SP001'; |
db.book.count({ "publisher.name": "SitePoint"}); This presumes denormalized documents are used. |
return the number of book format types | |
SELECT format, COUNT(1) AS totalFROM bookGROUP BY format; |
db.book.aggregate([ { $group: { _id: "$format", total: { $sum: 1 } } }]); This is known as aggregation: a new set of documents is computed from an original set. |
delete all SitePoint books | |
DELETE FROM bookWHERE publisher_id = 'SP001'; Alternatively, it’s possible to delete the publisher record and have this cascade to associated book records if foreign keys are specified appropriately. |
db.book.remove({ "publisher.name": "SitePoint"}); |
SQL VS NoSQL 表现
或许最具有争议性的比较是:通常情况下,NoSQL比SQL语言更快。这并没有什么好震惊的,NoSQL中更加简单的非规范化存储允许我们在一次查询中得到特定项的所有信息。不需要使用SQL中复杂的JOIN操作。
也就是说,你的项目的设计和数据的需求会有很大的影响。一个好的SQL数据库的设计的表现一定会比一个设计不好的NoSQL数据库性能好很多,反之亦然。
SQL VS NoSQL 规模
随着数据量的增长,我们或许会发现有必要将负载分配到到不同的服务器上。对于基于SQL语言的开发的系统是非常困难的。如何分配相关的数据?集群是一种最简单可能的解决方案,多个服务器访问同一个中央存储器—及时是这样也会有许多的问题。
NoSQL的简单的数据模型能够简化其过程,许多NoSQL数据库在一开始就构建了解决大规模数据的功能。这仅仅是一个概括,如果你遇到了这样的问题应该去寻求专家的帮助。
SQL VS NoSQL 可行性
最后,我们考虑一下安全性和系统性的问题。流行的NoSQL数据库已经存在好几年了,它们展现的问题可能会比成熟的关系型数据库多。许多问题都已经被发现了,但是所有的问题都指向了同一个问题:了解程度。
开发人员和系统管理员对于管理新型数据库有很少的经验,因此会产生许多问题。选择NoSQL是因为感觉它比较新颖,或者你想要避免数据模式的设计,都会在将来带来一些问题。
SQL VS NoSQL 总结
SQL和NoSQL数据库只是用不同的方式来完成相同的事情。我们可能会先选择其中之一然后更换到另一个上,但是在选择之前制定一个计划将会节约许多的时间和金钱。
适合使用SQL开发的项目:
- 可以预先定义逻辑相关的离散数据的需求
- 数据一致性是必要的
- 具有良好的开发者经验和技术支持的标准的成熟技术
适合使用NoSQL开发的项目:
- 不相关,不确定和逐步发展的数据需求
- 更简单或者更宽松的能够快速开始编程的项目
- 速度和可扩展性至关重要的
在我们的例子中,一个关系型数据库是一种更好的选择— 尤其是当我们需要引入强大的事务支持的电子商务设备。在接下来的一篇文章中,我们将讨论更多的项目场景,并确定使用一个SQL或NoSQL数据库是否是最好的解决方案。