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數據庫是否是最好的解決方案。