在前一篇文章中,我們討論了 SQL 與 NoSQL 數(shù)據(jù)庫之間基本的區(qū)別。接下來,我們我們將應(yīng)用我們在特定場景中的知識(shí)來確定最佳的選擇。
回顧一下:
SQL 數(shù)據(jù)庫:
- 在表中存儲(chǔ)相關(guān)聯(lián)的數(shù)據(jù)
- 在使用之前需要定義表的一個(gè)模式
- 鼓勵(lì)標(biāo)準(zhǔn)化減少數(shù)據(jù)冗余
- 支持從多個(gè)表中檢索相關(guān)數(shù)據(jù)表連接在一個(gè)單一的命令
- 實(shí)現(xiàn)數(shù)據(jù)完整性規(guī)則
- 提供事務(wù)使兩個(gè)或兩個(gè)以上的成功或失敗的數(shù)據(jù)更改作為一個(gè)原子單元
- 可以擴(kuò)展(有一些努力)
- 使用一個(gè)強(qiáng)聲明性語言查詢
- 提供足夠的支持,專業(yè)技能和工具。
NoSQL 數(shù)據(jù)庫
- 將相關(guān)聯(lián)的數(shù)據(jù)存儲(chǔ)在類似 JSON 格式,名稱-值
- 可以保存沒有指定格式的數(shù)據(jù)
- 通常必須規(guī)范化,所以一個(gè)項(xiàng)目的信息包含在一個(gè)文檔里
- 應(yīng)該不需要連接(假設(shè)使用規(guī)范化的文檔)
- 允許任何數(shù)據(jù)被保存在任何時(shí)候任何地方,不需要驗(yàn)證
- 保證更新一個(gè)文檔 – 但不是多個(gè)文檔
- 提供出色的性能和可伸縮性
- 使用 JSON 數(shù)據(jù)對象查詢
- 是一個(gè)新的、令人興奮的技術(shù)。
SQL 數(shù)據(jù)庫是一個(gè)理想的項(xiàng)目,確定好了需求和健壯的數(shù)據(jù)的完整性是至關(guān)重要的。NoSQL 數(shù)據(jù)庫是無關(guān)理想,不確定的或者不斷變化的數(shù)據(jù)需求 ,在速度和可伸縮性上更重要。 簡單的術(shù)語:
- SQL 是數(shù)字。它最適合明確的定義,精確規(guī)范的獨(dú)立項(xiàng)目。典型的使用案例是在線商城和銀行系統(tǒng)。
- NoSQL 是模擬。它最適合無固定要求的組織數(shù)據(jù)。典型的使用案例是社交網(wǎng)絡(luò),客戶管理和網(wǎng)絡(luò)分析系統(tǒng)。
一些項(xiàng)目要精準(zhǔn)的符合。如果你有較淺的話,任何一種選擇都是可行的,或者自然的非規(guī)范數(shù)據(jù)。但是請注意這些簡化示例場景與全面的概括!你比我更了解你的項(xiàng)目,我不建議切換從SQL到NoSQL或反之亦然,除非它提供了可觀的效益。這是你的選擇。在項(xiàng)目的開始要考慮利弊,你不能出錯(cuò)。
場景一:一個(gè)聯(lián)系人列表
讓我們重新發(fā)明輪子,實(shí)現(xiàn)一個(gè)基于sql的通訊錄系統(tǒng)。我們最初接觸表的時(shí)候,天真的定義以下字段:
- id (主鍵ID)
- title (標(biāo)題)
- firstname (姓)
- lastname (名)
- gender (性別)
- telephone (電話)
- email (郵箱)
- address1 (地址1)
- address2 (地址2)
- address3 (地址3)
- city (城市)
- region (區(qū)/縣)
- zipcode (郵政編碼)
- country (國家)
問題一: 很少人只有一個(gè)電話號(hào)碼。我們可能需要至少三個(gè)號(hào)碼:一個(gè)座機(jī),一個(gè)移動(dòng)電話,一個(gè)工作電話。但是有多少個(gè)號(hào)碼無關(guān)緊要——有些人、有些地方需要更多。讓我們創(chuàng)建一個(gè)單獨(dú)的 telephone 表,這樣的話他們想要多少聯(lián)系人都可以。這也讓我們的數(shù)據(jù)標(biāo)準(zhǔn)化了——我們不需要沒有號(hào)碼的聯(lián)系人顯示為NULL。
- contact_id
- name (文本,例如座機(jī)號(hào),工作手機(jī)等)
- number
問題二:Email地址有同樣的問題,因此我們也創(chuàng)建一個(gè)類似的 email 表:
- contact_id
- name (text such as home email, work email, etc.)
- address
問題三:我們可能不想輸入一個(gè)(地理位置的)地址,或者我們想輸入多個(gè)地址,工作地,家里,度假住所等。因此我們需要一個(gè)新的 address 表:
- contact_id
- name (text such as home, office, etc.)
- address1
- address2
- address3
- city
- region
- zipcode
- country
我們原來的 contact 表簡化成:
- id
- title
- firstname
- lastname
- gender
太棒了——我們有了一個(gè)能存放任意聯(lián)系人的任意多個(gè)電話號(hào)碼,Email 地址和住址的標(biāo)準(zhǔn)化數(shù)據(jù)庫。不幸的是……
Schema是固定不變的
我們沒有考慮到聯(lián)系人的中間名字、出生日期、公司或職位。我們添加多少字段都沒關(guān)系,我們很快會(huì)受到更新的需求要添加備注、紀(jì)念日、關(guān)系狀態(tài)、社交媒體賬號(hào)、內(nèi)腿測量值、最喜歡的奶酪類型等字段。預(yù)測所有選項(xiàng)是不可能的,因此我們可能需要一個(gè) otherdata 表,用來處理名字-值對。
數(shù)據(jù)是碎片化的
對開發(fā)者或者系統(tǒng)管理員來說,檢查數(shù)據(jù)庫并不容易。程序邏輯會(huì)變得更慢、更復(fù)雜,因?yàn)槔脝蝹€(gè) SELECT 和多個(gè) JOIN 語句查詢聯(lián)系人數(shù)據(jù)不太實(shí)際。(你可以這么做,但是結(jié)果可能需要包含 telephone,email,和 address字段的每一種組合:如果有個(gè)聯(lián)系人有三個(gè)電話號(hào)碼,五個(gè)Email地址和兩個(gè)住址,那么SQL查詢將會(huì)產(chǎn)生30條結(jié)果。) 最后,全文搜索很困難。如果有人輸入字符串”SitePoint”,我們必須檢查所有的表,看看它是否為聯(lián)系人名字、電話、Email或者住址的一部分,并且需要做相應(yīng)的排序。如果你用過WordPress的搜索功能,你就會(huì)明白這有多虐心。
選擇NoSQL
我們的聯(lián)系人數(shù)據(jù)關(guān)注的是人。他們難以預(yù)測,在不同的時(shí)間有不同的需求。使用NoSQL數(shù)據(jù)庫,聯(lián)系人列表將會(huì)從中受益。數(shù)據(jù)庫將一個(gè)聯(lián)系人的所有數(shù)據(jù)存儲(chǔ)在一個(gè)單獨(dú)的文檔里的contacts 集合里。
{
name: [
"Billy", "Bob", "Jones"
],
company: "Fake Goods Corp",
jobtitle: "Vice President of Data Management",
telephone: {
home: "0123456789",
mobile: "9876543210",
work: "2244668800"
},
email: {
personal: "bob@myhomeemail.net",
work: "bob@myworkemail.com"
},
address: {
home: {
line1: "10 Non-Existent Street",
city: "Nowhere",
country: "Australia"
}
},
birthdate: ISODate("1980-01-01T00:00:00.000Z"),
twitter: '@bobsfakeaccount',
note: "Don't trust this guy",
weight: "200lb",
photo: "52e86ad749e0b817d25c8892.jpg"}
在這個(gè)例子里,我們沒有存儲(chǔ)聯(lián)系人的頭銜或者性別,我們還添加了一些數(shù)據(jù),而這些數(shù)據(jù)不需要應(yīng)用到任何其他聯(lián)系人。沒關(guān)系——我們的NoSQL數(shù)據(jù)庫不會(huì)介意,我們還可以隨意添加或移除字段。
由于聯(lián)系人數(shù)據(jù)在單獨(dú)的文檔里,我們可以用一條查詢語句獲取一部分或全部信息。全文搜索也變得簡單;在MongoDB里,我們可以這樣定義 contact 中的所有文本字段的索引:
db.contact.createIndex({ "$**": "text" });
然后執(zhí)行全文搜索:
db.contact.find({
$text: { $search: "something" }});
場景二:社交網(wǎng)絡(luò)
社交網(wǎng)絡(luò)可能使用類似的聯(lián)系人數(shù)據(jù)存儲(chǔ),但是它會(huì)根據(jù)功能集合擴(kuò)展,比如關(guān)系鏈、狀態(tài)更新、發(fā)送消息和”贊“。這些功能可能會(huì)根據(jù)用戶需求來實(shí)現(xiàn)或者移除——無法預(yù)測它們會(huì)怎樣演進(jìn)。
另外:
- 大部分的數(shù)據(jù)更新都來自單個(gè)源:用戶。任何時(shí)候我們不太可能同時(shí)更新兩條或更多記錄,因此不要求類似事務(wù)控制的功能。
- 盡管有些用戶可能認(rèn)為,狀態(tài)更新失敗不可能引起系統(tǒng)崩潰或經(jīng)濟(jì)損失。應(yīng)用程序的接口和性能比數(shù)據(jù)完整性優(yōu)先級(jí)更高。
NoSQL看來是個(gè)好的方案。它允許我們快速地實(shí)現(xiàn)存儲(chǔ)不同類型數(shù)據(jù)的功能。例如,可以用單個(gè)文檔里的 status 集合替換所有用戶的過時(shí)的狀態(tài)更新。
{
user_id: ObjectID("65f82bda42e7b8c76f5c1969"),
update: [
{
date: ISODate("2015-09-18T10:02:47.620Z"),
text: "feeling more positive today"
},
{
date: ISODate("2015-09-17T13:14:20.789Z"),
text: "spending far too much time here"
}
{
date: ISODate("2015-09-17T12:33:02.132Z"),
text: "considering my life choices"
}
]}
文檔可能會(huì)變得很長,但我們可以獲取數(shù)組的子集,比如最近的更新。每個(gè)用戶的所有的歷史狀態(tài)記錄都能被快速搜索到。
現(xiàn)在假設(shè)我們想在發(fā)布更新的時(shí)候引入表情符號(hào)選擇。這涉及到給 update 數(shù)組里的新記錄添加圖引用。不像 SQL 存儲(chǔ),沒必要把之前消息里的表情符號(hào)置為 NULL——我們的程序邏輯可以顯示默認(rèn)圖片或者沒有圖片,如果沒有設(shè)置表情符號(hào)的話。
場景三:倉庫管理系統(tǒng)
考慮一個(gè)監(jiān)控倉庫貨物的系統(tǒng)。我們需要記錄:
- 送達(dá)倉庫并被分配到指定位置的物品
- 倉庫內(nèi)物品的移動(dòng),也就是重新整理庫存,以便讓同樣的物品放在相鄰的位置
- 訂單以及后續(xù)將物品搬出倉庫,準(zhǔn)備發(fā)貨
我們的數(shù)據(jù)需求:
- 通用的物品信息,比如包裝數(shù)量、尺寸和顏色等可被存儲(chǔ),但這些是我們可以識(shí)別并應(yīng)用到任何物品上的離散數(shù)據(jù)。我們不太可能關(guān)注細(xì)節(jié),例如筆記本處理器速度或者智能手機(jī)的電池壽命。
- 最小化出錯(cuò)的可能是必要的。我們不能讓物品憑空消失或者移到已經(jīng)有別的物品存放的位置。
- 簡單來說,我們在記錄物品從一個(gè)物理區(qū)域到另一個(gè)物理區(qū)域的轉(zhuǎn)移——或者從A位置移走,放到B位置。這是同一個(gè)動(dòng)作的兩次更新。
我們需要一個(gè)具備強(qiáng)制數(shù)據(jù)完整性和事務(wù)支持的健壯存儲(chǔ)系統(tǒng)。(當(dāng)前)只有 SQL 數(shù)據(jù)庫滿足這些需求。
表現(xiàn)自己!
我希望這些場景有所幫助,但是每個(gè)項(xiàng)目是不同的,最終,你需要做出自己的決定。(雖然,我們開發(fā)人員擅長于證明我們的技術(shù)選擇,不管他們有多好!)
最好的建議:顯露你自己盡可能多的技術(shù)。這些知識(shí)可以讓你對SQL或者NoSQL做出一個(gè)理性和情感上公正的判斷。祝您好運(yùn)。
英文原文:sitepoint.com,譯文:oschina.net
哈爾濱品用軟件有限公司致力于為哈爾濱的中小企業(yè)制作大氣、美觀的優(yōu)秀網(wǎng)站,并且能夠搭建符合百度排名規(guī)范的網(wǎng)站基底,使您的網(wǎng)站無需額外費(fèi)用,即可穩(wěn)步提升排名至首頁。歡迎體驗(yàn)最佳的哈爾濱網(wǎng)站建設(shè)。
