SQL vs NoSQL:如何選擇?

oschina發表於2015-09-28

在前一篇文章中,我們討論了 SQL 與 NoSQL 資料庫之間基本的區別。接下來,我們我們將應用我們在特定場景中的知識來確定最佳的選擇。

回顧一下:

SQL 資料庫:

  • 在表中儲存相關聯的資料
  • 在使用之前需要定義表的一個模式
  • 鼓勵標準化減少資料冗餘
  • 支援從多個表中檢索相關資料表連線在一個單一的命令
  • 實現資料完整性規則
  • 提供事務使兩個或兩個以上的成功或失敗的資料更改作為一個原子單元
  • 可以擴充套件(有一些努力)
  • 使用一個強宣告性語言查詢
  • 提供足夠的支援,專業技能和工具。

NoSQL 資料庫

  • 將相關聯的資料儲存在類似 JSON 格式,名稱-值
  • 可以儲存沒有指定格式的資料
  • 通常必須規範化,所以一個專案的資訊包含在一個文件裡
  • 應該不需要連線(假設使用規範化的文件)
  • 允許任何資料被儲存在任何時候任何地方,不需要驗證
  • 保證更新一個文件 - 但不是多個文件
  • 提供出色的效能和可伸縮性
  • 使用 JSON 資料物件查詢
  • 是一個新的、令人興奮的技術。

SQL 資料庫是一個理想的專案,確定好了需求和健壯的資料的完整性是至關重要的。NoSQL 資料庫是無關理想,不確定的或者不斷變化的資料需求 ,在速度和可伸縮性上更重要。 簡單的術語:

  • SQL 是數字。它最適合明確的定義,精確規範的獨立專案。典型的使用案例是線上商城和銀行系統。
  • NoSQL 是模擬。它最適合無固定要求的組織資料。典型的使用案例是社交網路,客戶管理和網路分析系統。

一些專案要精準的符合。如果你有較淺的話,任何一種選擇都是可行的,或者自然的非規範資料。但是請注意這些簡化示例場景與全面的概括!你比我更瞭解你的專案,我不建議切換從SQL到NoSQL或反之亦然,除非它提供了可觀的效益。這是你的選擇。在專案的開始要考慮利弊,你不能出錯。

場景一:一個聯絡人列表

讓我們重新發明輪子,實現一個基於sql的通訊錄系統。我們最初接觸表的時候,天真的定義以下欄位:

  • id (主鍵ID)
  • title (標題)
  • firstname (姓)
  • lastname (名)
  • gender (性別)
  • telephone (電話)
  • email   (郵箱)
  • address1 (地址1)
  • address2 (地址2)
  • address3 (地址3)
  • city       (城市)
  • region    (區/縣)
  • zipcode    (郵政編碼)
  • country    (國家)

問題一: 很少人只有一個電話號碼。我們可能需要至少三個號碼:一個座機,一個行動電話,一個工作電話。但是有多少個號碼無關緊要——有些人、有些地方需要更多。讓我們建立一個單獨的 telephone 表,這樣的話他們想要多少聯絡人都可以。這也讓我們的資料標準化了——我們不需要沒有號碼的聯絡人顯示為NULL。

  • contact_id
  • name (文字,例如座機號,工作手機等)
  • number

問題二:Email地址有同樣的問題,因此我們也建立一個類似的 email 表:

  • contact_id
  • name (text such as home email, work email, etc.)
  • address

問題三:我們可能不想輸入一個(地理位置的)地址,或者我們想輸入多個地址,工作地,家裡,度假住所等。因此我們需要一個新的 address 表:

  • contact_id
  • name (text such as home, office, etc.)
  • address1
  • address2
  • address3
  • city
  • region
  • zipcode
  • country

我們原來的 contact 表簡化成:

  • id
  • title
  • firstname
  • lastname
  • gender

太棒了——我們有了一個能存放任意聯絡人的任意多個電話號碼,Email 地址和住址的標準化資料庫。不幸的是……

Schema是固定不變的

我們沒有考慮到聯絡人的中間名字、出生日期、公司或職位。我們新增多少欄位都沒關係,我們很快會受到更新的需求要新增備註、紀念日、關係狀態、社交媒體賬號、內腿測量值、最喜歡的乳酪型別等欄位。預測所有選項是不可能的,因此我們可能需要一個 otherdata 表,用來處理名字-值對。

資料是碎片化的

對開發者或者系統管理員來說,檢查資料庫並不容易。程式邏輯會變得更慢、更復雜,因為利用單個 SELECT 和多個 JOIN 語句查詢聯絡人資料不太實際。(你可以這麼做,但是結果可能需要包含 telephone,email,和 address欄位的每一種組合:如果有個聯絡人有三個電話號碼,五個Email地址和兩個住址,那麼SQL查詢將會產生30條結果。)     最後,全文搜尋很困難。如果有人輸入字串”SitePoint”,我們必須檢查所有的表,看看它是否為聯絡人名字、電話、Email或者住址的一部分,並且需要做相應的排序。如果你用過WordPress的搜尋功能,你就會明白這有多虐心。

選擇NoSQL

我們的聯絡人資料關注的是人。他們難以預測,在不同的時間有不同的需求。使用NoSQL資料庫,聯絡人列表將會從中受益。資料庫將一個聯絡人的所有資料儲存在一個單獨的文件裡的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"}

在這個例子裡,我們沒有儲存聯絡人的頭銜或者性別,我們還新增了一些資料,而這些資料不需要應用到任何其他聯絡人。沒關係——我們的NoSQL資料庫不會介意,我們還可以隨意新增或移除欄位。

由於聯絡人資料在單獨的文件裡,我們可以用一條查詢語句獲取一部分或全部資訊。全文搜尋也變得簡單;在MongoDB裡,我們可以這樣定義 contact 中的所有文字欄位的索引:

db.contact.createIndex({ "$**": "text" });

然後執行全文搜尋:

db.contact.find({
  $text: { $search: "something" }});

場景二:社交網路

社交網路可能使用類似的聯絡人資料儲存,但是它會根據功能集合擴充套件,比如關係鏈、狀態更新、傳送訊息和”贊“。這些功能可能會根據使用者需求來實現或者移除——無法預測它們會怎樣演進。

另外:

  • 大部分的資料更新都來自單個源:使用者。任何時候我們不太可能同時更新兩條或更多記錄,因此不要求類似事務控制的功能。
  • 儘管有些使用者可能認為,狀態更新失敗不可能引起系統崩潰或經濟損失。應用程式的介面和效能比資料完整性優先順序更高。

NoSQL看來是個好的方案。它允許我們快速地實現儲存不同型別資料的功能。例如,可以用單個文件裡的 status 集合替換所有使用者的過時的狀態更新。

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

文件可能會變得很長,但我們可以獲取陣列的子集,比如最近的更新。每個使用者的所有的歷史狀態記錄都能被快速搜尋到。

現在假設我們想在釋出更新的時候引入表情符號選擇。這涉及到給 update 陣列裡的新記錄新增圖引用。不像 SQL 儲存,沒必要把之前訊息裡的表情符號置為 NULL——我們的程式邏輯可以顯示預設圖片或者沒有圖片,如果沒有設定表情符號的話。

場景三:倉庫管理系統

考慮一個監控倉庫貨物的系統。我們需要記錄:

  • 送達倉庫並被分配到指定位置的物品
  • 倉庫內物品的移動,也就是重新整理庫存,以便讓同樣的物品放在相鄰的位置
  • 訂單以及後續將物品搬出倉庫,準備發貨

我們的資料需求:

  1. 通用的物品資訊,比如包裝數量、尺寸和顏色等可被儲存,但這些是我們可以識別並應用到任何物品上的離散資料。我們不太可能關注細節,例如筆記本處理器速度或者智慧手機的電池壽命。
  2. 最小化出錯的可能是必要的。我們不能讓物品憑空消失或者移到已經有別的物品存放的位置。
  3. 簡單來說,我們在記錄物品從一個物理區域到另一個物理區域的轉移——或者從A位置移走,放到B位置。這是同一個動作的兩次更新。

我們需要一個具備強制資料完整性和事務支援的健壯儲存系統。(當前)只有 SQL 資料庫滿足這些需求。

表現自己!

我希望這些場景有所幫助,但是每個專案是不同的,最終,你需要做出自己的決定。(雖然,我們開發人員擅長於證明我們的技術選擇,不管他們有多好!)

最好的建議:顯露你自己儘可能多的技術。這些知識可以讓你對SQL或者NoSQL做出一個理性和情感上公正的判斷。祝您好運。

相關文章