資訊人筆記

Work hard, Have fun, Make history!

使用者工具

網站工具


側欄

database:sql_vs_nosql

SQL vs NoSQL: The Differences

0x00 前言

這篇內容多為翻譯,除了自己紀錄外,也希望能幫到更多的資訊朋友。有興趣也可以在在參考資料找到原文,如有勘誤請多包涵

– – – –

SQL (Structured Query Language) databases (Relational Databases) 成為主要的資料儲存機制已經持續了 40 年以上,在 1990s 後期更因為 web application 的發展與開源專案的觀念興起而被大量的使用,這類型的資料庫包含 MySQL, PostgreSQL, and SQLite.

NoSQL databases (Non-relational Databases) 則在 1960s 年代就存在,但一直到了最近隨著 MongoDB, CouchDB, Redis and Apache Cassandra 等 NoSQL databases 的發展才逐漸熱門起來

在網路上我們可以找到一些文章在解釋如何使用 SQL/NoSQL 的某些功能或特性,但較少提到為何你該選擇 SQL/NoSQL 而非另外一個

在這篇文章中我們會涵蓋 SQL/NoSQL 根本上的差異,在我們的範例中會以主流的 MySQL SQL and MongoDB NoSQL database 為例,其他 SQL/NoSQL 則是大同小異


0x01 Myths of SQL/NoSQL

Myth: NoSQL 是用來取代 SQL 的?

不,雖然 SQL 和 NoSQL 透過不同方式運作,但事實上他們的目的都一樣:儲存資料

NoSQL 隨著發展逐漸成為顯學,較常出現在技術文章標頭或是讓人感覺較新穎,但他並不是取代 SQL 的新技術

Myth: NoSQL 比 SQL 好或糟?

這要依照專案需求而定,有些專案適合採用 NoSQL,有些則適合 SQL

Myth: SQL 或 NoSQL 是可以清楚區分的?

這不必然正確,有些 SQL database 可能採用一些 NoSQL 的特性,反之亦然

Myth: language/framework 決定了資料庫的類型?

我們習慣採用某些技術組合,例如

  • LAMP: Linux, Apache, MySQL (SQL), PHP
  • MEAN: MongoDB (NoSQL), Express, Angular, Node.js
  • .NET, IIS and SQL Server
  • Java, Apache and Oracle

這些技術之所以會被組合提出通常是有歷史、商業原因並經過驗證可行的

但這並不是絕對標準,你可以在 PHP 或 .NET 專案中使用 MongoDB NoSQL database,或是用 Node.js 搭配 SQL Server

雖然特定技術組合能夠搜尋到較多教學與技術文件,但資料庫使用的應該是依專案需求決定而非語言

That said, don’t make life purposely difficult for yourself! Choosing an unusual technology combination or a mix of SQL and NoSQL is possible, but you’ll find it tougher to find support and employ experienced developers.


0x02 SQL Tables vs NoSQL Documents

SQL databases 用 data tables 來儲存相關連的資料

例如我們有一個線上書局,那書的資料可以儲存在一張叫做 book 的 table

ISBN title author format price
9780992461225 JavaScript: Novice to Ninja Darren Jones ebook 29.00
9780994182654 Jump Start Git Shaumik Daityari ebook 29.00

每一欄都是一筆不同的書籍紀錄

data tables 的格式設計是嚴格的,這表示你無法使用同一張資料表紀錄欄位不同的資料,或是在欄位值應該是數字的地方填入字串


NoSQL databases 是以類似 JSON, 欄位與數值成對(JSON-like, field-value pair) 的 document 去儲存資料

例如下面這個 document:

{
  ISBN: 9780992461225,
  title: "JavaScript: Novice to Ninja",
  author: "Darren Jones",
  format: "ebook",
  price: 29.00
}

類似的 documents 可以儲存在一個 collection 中,collection 比較類似 SQL 的 table

然而在 document 中我們可以儲存任何資料,不像 data tables 那樣嚴格

例如:

{
  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." }
  ]
}

這邊的 review 就使用了一個 collection 資料,而每個 document 間的 field-value pair 也不需要完全一致


SQL tables 建立了比較嚴格的資料模板,資料格式上較不容易出錯

NoSQL document 則較彈性且寬容,允許我們在任何地方儲存任何資料,但相對比較可能產生資料一致性的問題


0x03 SQL Schema vs NoSQL Schemaless

在 SQL database 中,若未在 schema 中定義好 tables 和 field types 等資訊前是無法插入資料的

schema 還包含一些其他的資訊如:

  • primary keys: 獨一無二的識別碼,像是上面 book table 的 ISBN 欄位
  • indexes: 這是用來加速搜尋的索引欄位
  • relationships: 資料欄位間的邏輯連結
  • 其他功能如 triggers, stored procedures

在你的程式邏輯可以操作資料之前,我們必須先設計並實作 SQL database data schema


在 NoSQL database 中,資料可以在任何時間被存放在任何地方

不需要事先設計或定義好 document 或 collection

以 MamgoDB 舉例,下面這段敘述可以在新的 book collection 中建立新的 document,即使他先前並未被建立過

db.book.insert(
  ISBN: 9780994182654,
  title: "Jump Start Git",
  author: "Shaumik Daityari",
  format: "ebook",
  price: 29.00
);

MongoDB 會自動建立獨一無二的 _id 值給 collection 中的每個 document, 至於 indexes 如果有需要則可事後再另外定義

NoSQL database 可能更適合一些在初始階段難以定義清楚資料庫格式的專案,但若因此忽略設計一個好資料庫的重要性可能導致後續的更多麻煩


0x04 SQL Normalization vs NoSQL Denormalization

假設我們要在我們的書局資料庫增加出版商 publisher 的資訊

一個出版商可能出版多本書,因此我們在 SQL database 新增一張 publisher table:

id name country email
SP001 SitePoint Australia feedback@sitepoint.com

接著我們可以在 book table 增加一個 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

我們不需要在每本書都重複紀錄出版社的資訊,只需要參照到另一張 table,可以有效減少冗餘資料

除此之外我們也可以在不改變 book table 的情況下去更新出版商的資料

這種技巧就稱為 normalization


我們也可以將 normalization 技巧使用在 NoSQL

如下是一個 book collection 中的一個 document

{
  ISBN: 9780992461225,
  title: "JavaScript: Novice to Ninja",
  author: "Darren Jones",
  format: "ebook",
  price: 29.00,
  publisher_id: "SP001"
}

他參照到 publisher collection 中的一個 document

{
  id: "SP001"
  name: "SitePoint",
  country: "Australia",
  email: "feedback@sitepoint.com"
}

然而我們可能傾向 denormalize 我們的 document 並在每個 book document 中重複 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"
  }
}

denormalize 的做法會使 NoSQL 的 query 速度更快

但在更新多筆出版商資訊時就會明顯較慢


0x05 SQL Relational JOIN vs NoSQL

SQL 的查詢中提供了 JOIN 這個有力的語法,我們可以在一句 SQL statement 取得多張 tables 的相關資料

例如:

SELECT book.title, book.author, publisher.name
FROM book
LEFT JOIN book.publisher_id ON publisher.id;

這會回傳所有 book title, author 和相關的 publisher name

NoSQL 沒有類似 JOIN 的語法

如果我們如上對 collection 做了 normalized,那我們必須先取出所有 book documents 和所有相關聯的 publisher documents,再透過程式邏輯手動連結它們

這也是為何多數 NoSQL 會採用 denormalization 的方式來儲存資料的原因


0x06 SQL vs NoSQL Data Integrity

foreign key 是一個 (或多個) 指向其它資料表中主鍵的欄位,它限制欄位值只能來自另一個資料表的主鍵欄位

多數的 SQL databases 使用 foreign key constraints 來確保資料參考的完整性

在我們的線上書局資料庫中,這個特性表示:

  • 每一筆 book 紀錄有一個合理的 publisher_id,並會對應到 publisher table 的一個實體
  • publisher 有被一或多筆 book 記錄參照到時候不允許被移除

schema 遵循這個規則,避免造成非法資料或 orphan records


NoSQL 中則沒有強制要求 data integrity,我們可以在不理會其他 document 下任意的儲存資料

理想情況下是能將一筆資料的所有相關訊息都放在一個 document 中,不與其他 document 相關聯


0x07 SQL vs NoSQL Transactions

在 SQL databases 中,兩個以上的資料更新動作可以在一個 transaction 內一起執行

這樣的包裝是一種 all-or-nothing 的執行,可以保證多個動作一起成功或失敗

舉例來說,如果我們的線上書局有 orderstock tables

當我們賣出一本書,我們在 order table 新增一筆資料並更新 stock table

若兩個動作是獨立執行的,則可能有一個成功一個失敗,這在資料的一致性與同步上就會出現狀況

所以將多個動作包含在一個 transaction 內便可保證一併成功或失敗


在 NoSQL database 中,更新單一 document 是 atomic action

換句話說若我們更新了 document 內的三個值,這三個值可可以確保一起成功被改變,或是失敗三個都沒變

然而,在多個 documents 間並沒有等同於 transaction 的操作

雖然有 transaction-like options 但目前來說這是必須在程式中手動處理的


0x08 SQL vs NoSQL CRUD Syntax

Creating, Reading Updating 和 Deleting data 是資料庫的最基本功能

在本質上

  • SQL 是一種輕量化聲明性語言,雖然不同資料庫系統在於法實作上有少許差異,但他簡單強大,並有一個國際通用標準
  • NoSQL 則是使用 JavaScripty-looking queries with JSON-like arguments,基本操作簡單,不過巢狀的 JSON 會越來越複雜,查詢上也會跟著複雜

下面是一個比較表:

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 book
SET price = 19.99
WHERE ISBN = '9780992461256'
db.book.update(
  { ISBN: '9780992461256' },
  { $set: { price: 19.99 } }
);
return all book titles over $10
SELECT title FROM book
WHERE 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 book
WHERE publisher_id = 'SP001';
db.book.count({
  "publisher.name": "SitePoint"
});
return the number of book format types
SELECT format, COUNT(1) AS `total`
FROM book
GROUP 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 book
WHERE 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"
});

0x09 SQL vs NoSQL Performance

這可能是最有爭議的一個比較

普遍的說法是 NoSQL 速度會比 SQL 快

這並不意外,NoSQL 採用 denormalized 的儲存方式可以讓我們再一次請求間取回所有相關的資訊而不需要額外 JOIN 或是做複雜的 SQL queries

另外一說是你的專案設計與資料請求會有更大的影響,設計良好的 SQL databases 會比設計不良的 NoSQL databases 有更佳的效能,反之亦然


0x0a SQL vs NoSQL Scaling

當我們的資料庫逐漸成長,我們會需要將資料庫分散到多台伺服器來減輕負擔

這對 SQL-based systems 是一項困難的挑戰,我們要如何分配相關聯的資料

Clustering 是一個可行的方式,多台 server 同樣存取一個資料儲存中心

但即便如此這也是個挑戰


NoSQL databases 上可能相對容易,許多 databases 從一開始就有 scaling functionality

不過這些只是推論,如果真的遇到了這方面問題,還是建議不吝請專家為你處理


0x0b SQL vs NoSQL Summary

SQL 和 NoSQL databases 只是用不同方式在做相同的事,雖然可以再選擇後再改變,但專案的事先的計畫可以幫我們省下時間與金錢

適合 SQL 的專案

  • logical related discrete data requirements which can be identified up-front
  • data integrity is essential
  • standards-based proved technology with good developer experience and support.

適合 NoSQL 的專案

  • unrelated, indeterminate or evolving data requirements
  • simpler or looser project objectives, able to start coding immediately
  • speed and scalability are imperative.

0x0c 參考資料

database/sql_vs_nosql.txt · 上一次變更: 2018/11/19 08:00 (外部編輯)