NoSQL數據建模技術

原文來自「NoSQL Data Modeling Techniques」,由酷殼網陳皓編譯《NoSQL數據建模技術》。這篇文章看完以後,你可能會對NoSQL的數據結構會有些感受。個人感受是,關係型數據庫想把一致性,完整性,索引,CRUD都幹好,NoSQL只幹某一種事,可是犧牲了不少別的東西。整體來講,我以爲NoSQL更適合作Cache。html

下面是正文:正則表達式

NoSQL數據庫常常被用做不少非功能性的地方,如,擴展性,性能和一致性的地方。這些NoSQL的特性在理論和實踐中都正在被大衆普遍地研究着,研究的熱點正是那些和性能分佈式相關的非功能性的東西,咱們都知道CAP 理論被很好地應用於了NoSQL系統中(陳皓注:CAP即,一致性(Consistency),可用性(Availability),分區容忍性(Partition tolerance),在分佈式系統中,這三個要素最多隻能同時實現兩個,而NoSQL通常放棄的是一致性)。但在另外一方面,NoSQL的數據建模技術卻由於缺少像關係型數據庫那樣的基礎理論沒有被世人很好地研究。這篇文章從數據建模方面對NoSQL家族進行了比較,並討論幾個常見的數據建模技術。算法

要開始討論數據建模技術,咱們不得不或多或少地先系統地看一下NoSQL數據模型的成長的趨勢,以此咱們能夠了一些他們內在的聯繫。下圖是NoSQL家族的進化圖,咱們能夠看到這樣的進化:Key-Value時代,BigTable時代,Document時代,全文搜索時代,和Graph數據庫時代:(陳皓注:注意圖中SQL說的那句話,NoSQL再這樣發展下去就是SQL了,哈哈。)sql

NoSQL Data Modelsshell

首先,咱們須要注意的是SQL和關係型數據模型已存在了很長的時間,這種面向用戶的天然性意味着:數據庫

  • 最終用戶通常更感興趣於數據的聚合顯示,而不是分離的數據,這主要經過SQL來完成。
  • 咱們沒法經過人手工控制數據的併發性,完整性,一致性,或是數據類型校驗這些東西的。這就是爲何SQL須要在事務,二維表結構(schema)和外表聯合上作不少事。

另外一方面,SQL可讓軟件應用程序在不少狀況下不須要關心數據庫的數據聚合,和數據完整性和有效性進行控制。而若是咱們去除了數據一致性,完整性這些東西,會對性能和分佈存儲有着重的幫助。正由於如此,咱們纔有數據模型的進化:數組

  • Key-Value鍵值對存儲是很是簡單而強大的。下面的不少技術基本上都是基於這個技術開始發展的。可是,Key-Value有一個很是致命的問題,那就是若是咱們須要查找一段範圍內的key。(陳皓注:學過hash-table數據結構的人都應該知道,hash-table是非序列容器,其並不像數組,連接,隊列這些有序容器,咱們能夠控制數據存儲的順序)。因而,有序鍵值(Ordered Key-Value)數據模型被設計出來解決這一限制,來從根本上提升數據集的問題。
  • Ordered Key-Value有序鍵值模型也很是強大,可是,其也沒有對Value提供某種數據模型。一般來講,Value的模型能夠由應用負責解析和存取。這種很不方便,因而出現了BigTable類型的數據庫,這個數據模型其實就是map裏有map,map裏再套map,一層一層套下去,也就是層層嵌套的key- value(value裏又是一個key-value),這種數據庫的Value主要經過「列族」(column families),列,和時間截來控制版本。(陳皓注:關於時間截來對數據的版本控制主要是解決數據存儲併發問題,也就是所謂的樂觀鎖,詳見《多版本併發控制(MVCC)在分佈式系統中的應用》)
  • Document databases 文檔數據庫 改進了BigTable模型,並提供了兩個有意義的改善。第一個是容許Value中有主觀的模式(scheme),而不是map套map。第二個是索引。Full Text Search Engines全文搜索引擎能夠被看做是文檔數據庫的一個變種,他們能夠提供靈活的可變的數據模式(scheme)以及自動索引。他們之間的不一樣點主要是,文檔數據庫用字段名作索引,而全文搜索引擎用字段值作索引。
  • Graph data models圖式數據庫 能夠被認爲是這個進化過程當中從Ordered Key-Value數據庫發展過來的一個分支。圖式數據庫容許構建議圖結構的數據模型。它和文檔數據庫有關係的緣由是,它的不少實現容許value能夠是一個map或是一個document。

NoSQL數據模型摘要

本文剩下的章節將向你介紹數據建模的技術實現和相關模式。可是,在介紹這些技術以前,先來一段序言:服務器

  • NoSQL數據模型設計通常從業務應用的具體數據查詢入手,而不是數據間的關係:
  • 關係型的數據模型基本上是分析數據間的結構和關係。其設計理念是: 「What answers do I have?」
  • NoSQL數據模型基本上是從應用對數據的存取方式入手,如:我須要支持某種數據查詢。其設計理念是 」What questions do I have?」
  • NoSQL數據模型設計比關係型數據庫須要對數據結構和算法的更深的瞭解。在這篇文章中我會和你們說那些盡人皆知的數據結構,這些數據結構並不僅是被NoSQL使用,可是對於NoSQL的數據模型卻很是有幫助。
  • 數據冗餘和反規格化是一等公民。
  • 關係型數據庫對於處理層級數據和圖式數據很是的不方便。NoSQL用來解決圖式數據明顯是一個很是好的解決方案,幾乎全部的NoSQL數據庫能夠很強地解決此類問題。這就是爲何這篇文章專門拿出一章來講明層級數據模型。

下面是NoSQL的分類表,也是我用來寫這篇文章時作實踐的產品:數據結構

  • Key-Value 存儲: Oracle Coherence, Redis, Kyoto Cabinet
  • 類BigTable存儲: Apache HBase, Apache Cassandra
  • 文檔數據庫: MongoDB, CouchDB
  • 全文索引: Apache Lucene, Apache Solr
  • 圖數據庫: neo4j, FlockDB

概念技術Conceptual Techniques

這一節主要介紹NoSQL數據模型的基本原則。併發

(1)反規格化Denormalization

反規格化Denormalization能夠被認爲是把相同的數據拷貝到不一樣的文檔或是表中,這樣就能夠簡化和優化查詢,或是正好適合用戶的某中特別的數據模型。這篇文章中所說的絕大多數技術都或多或少地導向了這一技術。

整體來講,反規格化須要權衡下面這些東西:

  • 查詢數據量 /查詢IO VS 總數據量。使用反規格化,一方面能夠把一條查詢語句所須要的全部數據組合起來放到一個地方存儲。這意味着,其它不一樣不一樣查詢所須要的相同的數據,須要放在別不一樣的地方。所以,這產生了不少冗餘的數據,從而致使了數據量的增大。
  • 處理複雜度 VS 總數據量. 在符合範式的數據模式上進行錶鏈接的查詢,很顯然會增長了查詢處理的複雜度,尤爲對於分佈式系統來講更是。反規格化的數據模型容許咱們以方便查詢的方式來存構造數據結構以簡化查詢複雜度。

適用性:Key-Value Store 鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。

(2)聚合Aggregates

全部類型的NoSQL數據庫都會提供靈活的Schema(數據結構,對數據格式的限制):

  • Key-Value Stores 和 Graph Databases基本上來講不會Value的形式,因此Value能夠是任意格式。這樣一來,這使得咱們能夠任意組合一個業務實體的keys。好比,咱們有一個用戶賬號的業務實體,其能夠被以下這些key組合起來: UserID_name,UserID_email, UserID_messages等等。若是一個用戶沒有email或message,那麼相應也不會有這樣的記錄。
  • BigTable模型經過列集合來支持靈活的Schema,咱們稱之爲列族(column family)。BigTable還能夠在同一記錄上出現不一樣的版本(經過時間截)。
  • Document databases文檔數據庫是一種層級式的「去Schema」的存儲,雖然有些這樣的數據庫容許檢驗須要保存的數據是否知足某種Schema。

靈活的Schema容許你能夠用一種嵌套式的內部數據方式來存儲一組有關聯的業務實體(陳皓注:相似於JSON這樣的數據封裝格式)。這樣能夠爲咱們帶來兩個好處。

  • 最小化「一對多」關係——能夠經過嵌套式的方式來存儲實體,這樣能夠少一些表聯結。
  • 可讓內部技術上的數據存儲更接近於業務實體,特別是那種混合式的業務實體。可能存於一個文檔集或是一張表中。

下圖示意了這兩種好處。圖中描給了電子商務中的商品模型(陳皓注:我記得我在「挑戰無處不在」一文中說到過電商中產品分類數據庫設計的挑戰)

  • 首先,全部的商品Product都會有一個ID,Price和Description。
  • 而後,咱們能夠知道不一樣的類型的商品會有不一樣的屬性。好比,做者是書的屬性,長度是牛仔褲的屬性。其些屬性多是「一對多」或是「多對多」的關係,如:唱片中的曲目。
  • 接下來,咱們知道,某些業務實體不可能使用固定的類型。如:牛仔褲的屬性並非全部的牌子都有的,並且,有些名牌還會搞很是特別的屬性。

對於關係型數據庫來講,要設計這樣的數據模型並不簡單,並且設計出來的絕對離優雅很遠很遠。而咱們NoSQL中靈活的Schema容許你使用一個聚合Aggregate (product) 能夠建出全部不一樣種類的商品和他們的不一樣的屬性:

Entity Aggregation

上圖中咱們能夠比較關係型數據庫和NoSQL的差異。可是咱們能夠看到在數據更新上,非規格化的數據存儲在性能和一致性上會有很大的影響,這就是咱們須要重點注意和不得不犧牲的地方

適用性: Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。

(3)應用層聯結Application Side Joins

表聯結基本上不被NoSQL支持。正如咱們前面所說的,NoSQL是「面向問題」而不是「面向答案」的,不支持表聯結就是「面向問題」的後果。表的聯結是在設計時被構造出來的,而不是在執行時建造出來的。因此,表聯結在運行時是有很大開銷的(陳皓注:搞過SQL表聯結的都知道笛卡爾積是什麼東西,大能夠在參看之前酷殼的「圖解數據庫表Joins」),可是在使用了Denormalization和Aggregates技術後,咱們基本不用進行表聯結,如:大家使用嵌套式的數據實體。固然,若是你須要聯結數據,你須要在應用層完成這個事。下面是幾個主要的Use Case:

  • 多對多的數據實體關係——常常須要被鏈接或聯結。
  • 聚合Aggregates並不適用於數據字段常常被改變的狀況。對此,咱們須要把那些常常被改變的字段分到另外的表中,而在查詢時咱們須要聯結數據。例如,咱們有個Message系統能夠有一個User實體,其包括了一個內嵌的Message實體。可是,若是用戶不斷在附加message,那麼,最好把message拆分到另外一個獨立的實體,但在查詢時聯結這User和Message這兩個實體。以下圖:

適用性: Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫,Graph Databases圖數據庫。

通用建模技術General Modeling Techniques

在本書中,咱們將討論NoSQL中各類不一樣的通用的數據建模技術。

(4)原子聚合Atomic Aggregates

不少NoSQL的數據庫(並非全部)在事務處理上都是短板。在某些狀況下,他們能夠經過分佈式鎖技術或是應用層管理的MVCC技術來實現其事務性(陳皓注:可參看本站的「多版本併發控制(MVCC)在分佈式系統中的應用」)可是,一般來講只能使用聚合Aggregates技術來保證一些ACID原則。

這就是爲何咱們的關係型數據庫須要有強大的事務處理機制——由於關係型數據庫的數據是被規格化存放在了不一樣的地方。因此,Aggregates聚合容許咱們把一個業務實體存成一個文檔、存成一行,存成一個key-value,這樣就能夠原子式的更新了:

Atomic Aggregates

固然,原子聚合Atomic Aggregates這種數據模型並不能實現徹底意義上的事務處理,可是若是支持原子性,鎖,或test-and-set指令,那麼,Atomic Aggregates是能夠適用的。

適用性Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。

(5)可枚舉鍵Enumerable Keys

也許,對於無順序的Key-Value最大的好處是業務實體能夠被容易地hash以分區在多個服務器上。而排序了的key會把事情搞複雜,可是有些時候,一個應用能從排序key中得到不少好處,就算是數據庫自己不提供這個功能。讓咱們來思考下email消息的數據模型:

  1. 一些NoSQL的數據庫提供原子計數器以容許生一些連續的ID。在這種狀況下,咱們可使用 userID_messageID 來作爲一個組合key。若是咱們知道最新的message ID,就能夠知道前一個message,也可能知道再前面和後面的Message。
  2. Messages能夠被打包。好比,天天的郵件包。這樣,咱們就能夠對郵件按指定的時間段來遍歷。

適用性Key-Value Store鍵值對數據庫

(6)降維Dimensionality Reduction

Dimensionality Reduction降維是一種技術能夠容許把一個多維的數據映射成一個Key-Value或是其它非多給的數據模型。

傳統的地理位置信息系統使用一些如「四分樹QuadTree」或「R-Tree」來作地理位置索引。這些數據結構的內容須要被在適當的位置更新,而且,若是數據量很大的話,操做成本會很高。另外一個方法是咱們能夠遍歷一個二維的數據結構並把其扁平化成一個列表。一個衆所周知的例子是Geohash(地理哈希)。一個Geohash使用「之字形」的路線掃描一個2維的空間,並且遍歷中的移動能夠被簡單地用0和1來表示其方向,而後在移動的過程當中產生0/1串。下圖展現了這一算法:(陳皓注:先把地圖分紅四份,經度爲第一位,緯度爲第二位,因而左邊的經度是0,右邊的是1,緯度也同樣,上面是爲1,下面的爲0,這樣,經緯度就能夠組合成01,11,00,10這四個值,其標識了四塊區域,咱們能夠如此不斷的遞歸地對每一個區域進行四分,而後能夠獲得一串1和0組成的字串,而後使用0-9,b-z去掉(去掉a, i, l, o)這32個字母進行base32編碼獲得一個8個長度的編碼,這就是Geohash的算法)

Geohash Index

Geohash的最強大的功能是使用簡單的位操做就能夠知道兩個區域間的距離,就像圖中所示(陳皓:proximity框着的那兩個,這個很像IP地址了)。Geohash把一個二維的座標生生地變成了一個一維的數據模型,這就是降維技術。BigTable的降維技術參看到文章後面的[6.1]。更多的關於Geohash和其它技術能夠參看[6.2] 和 [6.3]。

適用性: Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。

(7)索引表 Index Table

Index Table索引表是一個很是直白的技術,其能夠你在不支持索引的數據庫中獲得索引的好處。BigTable是這類最重要的數據庫。這須要咱們維護一個有相應存取模式的特別表。例如,咱們有一個主表存着用戶賬號,其能夠被UserID存取。某查詢須要查出某個城市裏全部的用戶,因而咱們能夠加入一張表,這張表用城市作主鍵,全部和這個城市相關的UserID是其Value,以下所示:

Index Table Example

可見,城市索引表的須要和對主表用戶表保持一致性,所以,主表的每個更新可能須要對索引表進行更新,否則就是一個批處理更新。不管哪一個方式,這都會損傷一些性能,由於須要保持一致性。

Index Table索引表能夠被認爲是關係型數據庫中的視圖的等價物。

適用性:BigTable數據庫。

(8)鍵組合索引 Composite Key Index

Composite key鍵組合是一個很經常使用的技術,對此,當咱們的數據庫支持鍵排序時能獲得極大的好處。Composite key組合鍵的拼接成爲第二排序字段可讓你構建出一種多維索引,這很像咱們以前說過的 Dimensionality Reduction降維技術。例如,咱們須要存取用戶統計。若是咱們須要根據不一樣的地區來統計用戶的分佈狀況,咱們能夠把Key設計成這樣的格式 (State:City:UserID),這樣一來,就使得咱們能夠經過State到City來按組遍歷用戶,特別是咱們的NoSQL數據庫支持在key上按區查詢(如:BigTable類的系統):

  1. SELECT Values WHERE state="CA:*" 
  2. SELECT Values WHERE city="CA:San Francisco*" 

Composite Key Index

適用性BigTable 數據庫。

(9)鍵組合聚合 Aggregation with Composite Keys

Composite keys鍵組合技術並不只僅能夠用來作索引,一樣能夠用來區分不用的類型的數據以支持數據分組。考慮一個例子,咱們有一個海量的日誌數組,這個日誌記錄了互聯網上的用戶的訪問來源。咱們須要計算從某一網站過來的獨立訪客的數量,在關係型數據庫中,咱們可能須要下面這樣的SQL查詢語句:

  1. SELECT count(distinct(user_id)) FROM clicks GROUP BY site  

咱們能夠在NoSQL中創建以下的數據模型:

Counting Unique Users using Composite Keys

這樣,咱們就能夠把數據按UserID來排序,咱們就能夠很容易把同一個用戶的數據(一個用戶並不會產生太多的event)進行處理,去掉那些重複的站點(使用hash table或是別的什麼)。另外一個可選的技術是,咱們能夠對每個用戶創建一個數據實體,而後把其站點來源追加到這個數據實體中,固然,這樣一來,數據的更新在性能相比之下會有必定損失。

適用性: Ordered Key-Value Store 排序鍵值對數據庫, BigTable風格的數據庫。

(10)反轉搜索 Inverted Search – 直接聚合 Direct Aggregation

這個技術更多的是數據處理技術,而不是數據建模技術。儘管如此,這個技術仍是會影響數據模型。這個技術最主要的想法是使用一個索引來找到知足某條件的數據,可是把數據聚合起須要使用全文搜索。仍是讓咱們來講一個示例。仍是用上面那個例子,咱們有不少的日誌,其中包括互聯網用戶和他們的訪問來源。讓咱們假定每條記錄都有一個UserID,還有用戶的種類 (Men,Women,Bloggers,等),以及用戶所在的城市,和訪問過的站點。咱們要乾的事是,爲每一個用戶種類找到知足某些條件(訪問源,所在城市,等)的的獨立用戶。

很明顯,咱們須要搜索那些知足條件的用戶,若是咱們使用反轉搜索,這會讓咱們把這事幹得很容易,如: {Category -> [user IDs]} 或 {Site -> [user IDs]}。使用這樣的索引,咱們能夠取兩個或多個UserID要的交集或並集(這個事很容易幹,並且能夠乾得很快,若是這些UserID是排好序的)。可是,咱們要按用戶種類來生成報表會變得有點麻煩,由於咱們用語句可能會像下面這樣

  1. SELECT count(distinct(user_id)) ... GROUP BY category  

但這樣的SQL很沒有效率,由於category數據太多了。爲了應對這個問題,咱們能夠創建一個直接索引 {UserID -> [Categories]}而後咱們用它來生成報表:

Counting Unique Users using Inverse and Direct Indexes

最後,咱們須要明白,對每一個UserID的隨機查詢是很沒有效率的。咱們能夠經過批查詢處理來解決這個問題。這意味着,對於一些用戶集,咱們能夠進行預處理(不一樣的查詢條件)。

適用性: Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。

層級式模型Hierarchy Modeling Techniques

(11)樹形聚合Tree Aggregation

樹形或是任意的圖(需反規格化)能夠被直接打成一條記錄或文檔存放。

  • 當樹形結構被一次性取出時這會很是有效率(如:咱們須要展現一個blog的樹形評論)
  • 搜索和任何存取這個實體都會存在問題。
  • 對於大多數NoSQL的實現來講,更新數據都是很不經濟的(相比起獨立結點來講)

Tree Aggregation

適用性:Key-Value鍵值對數據庫,Document Databases文檔數據庫

(12)鄰接列表 Adjacency Lists

Adjacency Lists鄰接列表是一種圖–每個結點都是一個獨立的記錄,其包含了全部的父結點或子結點。這樣,咱們就能夠經過給定的父或子結點來進行搜索。固然,咱們須要經過hop查詢遍歷圖。這個技術在廣度和深度查詢,以及獲得某個結點的子樹上沒有效率。

適用性:Key-Value鍵值對數據庫,Document Databases文檔數據庫

(13) Materialized Paths

Materialized Paths能夠幫助避免遞歸遍歷(如:樹形結構)。這個技術也能夠被認爲是反規格化的一種變種。其想法是爲每一個結點加上父結點或子結點的標識屬性,這樣就能夠不須要遍歷就知道全部的後裔結點和祖先結點了:

Materialized Paths for eShop Category Hierarchy

這個技術對於全文搜索引擎來講很是有幫助,由於其能夠容許把一個層級結構轉成一個文檔。上面的示圖中咱們能夠看到全部的商品或Men’s Shoes下的子分類能夠被一條很短的查詢語句處理——只須要給定個分類名。

Materialized Paths能夠存儲一個ID的集合,或是一堆ID拼出的字符串。後者容許你經過一個正則表達式來搜索一個特定的分支路徑。下圖展現了這個技術(分支的路徑包括告終點自己):

Query Materialized Paths using RegExp

適用性:Key-Value鍵值對數據庫,Document Databases文檔數據,Search Engines搜索引擎

(14)嵌套集 Nested Sets

Nested sets嵌套集是樹形結構的標準技術。它被普遍地用在了關係性數據庫中,它徹底地適用於Key-Value鍵值對數據庫和Document Databases文檔數據庫。這個技術的想法是把葉子結點存儲成一個數組,並經過使用索引的開始和結束來映射每個非葉子結點到一個葉子結點集,就以下圖所示同樣:

Modeling of eCommerce Catalog using Nested Sets

這樣的數據結構對於immutable data不變的數據有很是不錯的效率,由於其點內存空間小,而且能夠很快地找出全部的葉子結點而不須要樹的遍歷。儘管如此,在插入和更新上須要很高的性能成本,由於新的葉子結點須要大規模地更新索引。

適用性:Key-Value Stores鍵值數據庫,Document Databases文檔數據庫

(15)嵌套文檔扁平化:有限的字段名Nested Documents Flattening:Numbered Field Names

搜索引擎基本上來講和扁平文檔一同工做,如:每個文檔是一個扁平的字段和值的例表。這種數據模型的用來把業務實體映射到一個文本文檔上,若是你的業務實體有很複雜的內部結構,這可能會變得頗有挑戰。一個典型的挑戰是把一個有層級的文檔映映射出來。例如,文檔中嵌套另外一個文檔。讓咱們看看下面的示例:

Nested Documents Problem

上面的每個業務實體代碼一種簡歷。其包括了人名和一個技能列表。我把這個層級文檔映射成一個文本文檔,一種方法是建立Skill和Level字段。這個模型能夠經過技術或是等級來搜索一我的,而上圖標註的那樣的組合查詢則會失敗。(陳皓注:由於分不清Excellent是不是Math仍是Poetry上的)

在引用中的[4.6]給出了一種解決方案。其爲每一個字段都標上數字 Skill_i 和 Level_i,這樣就能夠分開搜索每個對(下圖中使用了OR來遍歷查找全部可能的字段):

Nested Document Modeling using Numbered Field Names

這樣的方式根本沒有擴展性,對於一些複雜的問題來講只會讓代碼複雜度和維護工做變大。

適用性:Search Engines全文搜索

(16)嵌套文檔扁平化:鄰近查詢 Nested Documents Flattening: Proximity Queries

在附錄[4.6]中給出了這個技術用來解決扁平層次文檔。它用鄰近的查詢來限制可被查詢的單詞的範圍。下圖中,全部的技能和等級被放在一個字段中,叫 SkillAndLevel,查詢中出現的「Excellent」和「Poetry」必需一個緊跟另外一個:

Nested Document Modeling using Proximity Queries

附錄[4.3]中講述了這個技術被用在Solr中的一個成功案例。

適用性:Search Engines全文搜索

(17) 圖結構批處理 Batch Graph Processing

Graph databases圖數據庫,如neo4j是一個出衆的圖數據庫,尤爲是使用一個結點來探索鄰居結點,或是探索兩個或少許結點前的關係。可是處理大量的圖數據是很沒有效率的,由於圖數據庫的性能和擴展性並非其目的。分佈式的圖數據處理能夠被MapReduce 和 Message Passing pattern來處理。如:在我前一篇的文章中的那個示例。這個方法可讓Key-Value stores, Document databases和BigTable-style databases適合於處理大圖。

相關文章
相關標籤/搜索