原文來自「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可讓軟件應用程序在不少狀況下不須要關心數據庫的數據聚合,和數據完整性和有效性進行控制。而若是咱們去除了數據一致性,完整性這些東西,會對性能和分佈存儲有着重的幫助。正由於如此,咱們纔有數據模型的進化:數組
本文剩下的章節將向你介紹數據建模的技術實現和相關模式。可是,在介紹這些技術以前,先來一段序言:服務器
下面是NoSQL的分類表,也是我用來寫這篇文章時作實踐的產品:數據結構
這一節主要介紹NoSQL數據模型的基本原則。併發
反規格化Denormalization能夠被認爲是把相同的數據拷貝到不一樣的文檔或是表中,這樣就能夠簡化和優化查詢,或是正好適合用戶的某中特別的數據模型。這篇文章中所說的絕大多數技術都或多或少地導向了這一技術。
整體來講,反規格化須要權衡下面這些東西:
適用性:Key-Value Store 鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。
全部類型的NoSQL數據庫都會提供靈活的Schema(數據結構,對數據格式的限制):
靈活的Schema容許你能夠用一種嵌套式的內部數據方式來存儲一組有關聯的業務實體(陳皓注:相似於JSON這樣的數據封裝格式)。這樣能夠爲咱們帶來兩個好處。
下圖示意了這兩種好處。圖中描給了電子商務中的商品模型(陳皓注:我記得我在「挑戰無處不在」一文中說到過電商中產品分類數據庫設計的挑戰)
對於關係型數據庫來講,要設計這樣的數據模型並不簡單,並且設計出來的絕對離優雅很遠很遠。而咱們NoSQL中靈活的Schema容許你使用一個聚合Aggregate (product) 能夠建出全部不一樣種類的商品和他們的不一樣的屬性:
Entity Aggregation
上圖中咱們能夠比較關係型數據庫和NoSQL的差異。可是咱們能夠看到在數據更新上,非規格化的數據存儲在性能和一致性上會有很大的影響,這就是咱們須要重點注意和不得不犧牲的地方。
適用性: Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。
表聯結基本上不被NoSQL支持。正如咱們前面所說的,NoSQL是「面向問題」而不是「面向答案」的,不支持表聯結就是「面向問題」的後果。表的聯結是在設計時被構造出來的,而不是在執行時建造出來的。因此,表聯結在運行時是有很大開銷的(陳皓注:搞過SQL表聯結的都知道笛卡爾積是什麼東西,大能夠在參看之前酷殼的「圖解數據庫表Joins」),可是在使用了Denormalization和Aggregates技術後,咱們基本不用進行表聯結,如:大家使用嵌套式的數據實體。固然,若是你須要聯結數據,你須要在應用層完成這個事。下面是幾個主要的Use Case:
適用性: Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫,Graph Databases圖數據庫。
在本書中,咱們將討論NoSQL中各類不一樣的通用的數據建模技術。
不少NoSQL的數據庫(並非全部)在事務處理上都是短板。在某些狀況下,他們能夠經過分佈式鎖技術或是應用層管理的MVCC技術來實現其事務性(陳皓注:可參看本站的「多版本併發控制(MVCC)在分佈式系統中的應用」)可是,一般來講只能使用聚合Aggregates技術來保證一些ACID原則。
這就是爲何咱們的關係型數據庫須要有強大的事務處理機制——由於關係型數據庫的數據是被規格化存放在了不一樣的地方。因此,Aggregates聚合容許咱們把一個業務實體存成一個文檔、存成一行,存成一個key-value,這樣就能夠原子式的更新了:
Atomic Aggregates
固然,原子聚合Atomic Aggregates這種數據模型並不能實現徹底意義上的事務處理,可是若是支持原子性,鎖,或test-and-set指令,那麼,Atomic Aggregates是能夠適用的。
適用性: Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。
也許,對於無順序的Key-Value最大的好處是業務實體能夠被容易地hash以分區在多個服務器上。而排序了的key會把事情搞複雜,可是有些時候,一個應用能從排序key中得到不少好處,就算是數據庫自己不提供這個功能。讓咱們來思考下email消息的數據模型:
適用性: Key-Value Store鍵值對數據庫。
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風格的數據庫。
Index Table索引表是一個很是直白的技術,其能夠你在不支持索引的數據庫中獲得索引的好處。BigTable是這類最重要的數據庫。這須要咱們維護一個有相應存取模式的特別表。例如,咱們有一個主表存着用戶賬號,其能夠被UserID存取。某查詢須要查出某個城市裏全部的用戶,因而咱們能夠加入一張表,這張表用城市作主鍵,全部和這個城市相關的UserID是其Value,以下所示:
Index Table Example
可見,城市索引表的須要和對主表用戶表保持一致性,所以,主表的每個更新可能須要對索引表進行更新,否則就是一個批處理更新。不管哪一個方式,這都會損傷一些性能,由於須要保持一致性。
Index Table索引表能夠被認爲是關係型數據庫中的視圖的等價物。
適用性:BigTable數據庫。
Composite key鍵組合是一個很經常使用的技術,對此,當咱們的數據庫支持鍵排序時能獲得極大的好處。Composite key組合鍵的拼接成爲第二排序字段可讓你構建出一種多維索引,這很像咱們以前說過的 Dimensionality Reduction降維技術。例如,咱們須要存取用戶統計。若是咱們須要根據不一樣的地區來統計用戶的分佈狀況,咱們能夠把Key設計成這樣的格式 (State:City:UserID),這樣一來,就使得咱們能夠經過State到City來按組遍歷用戶,特別是咱們的NoSQL數據庫支持在key上按區查詢(如:BigTable類的系統):
Composite Key Index
適用性: BigTable 數據庫。
Composite keys鍵組合技術並不只僅能夠用來作索引,一樣能夠用來區分不用的類型的數據以支持數據分組。考慮一個例子,咱們有一個海量的日誌數組,這個日誌記錄了互聯網上的用戶的訪問來源。咱們須要計算從某一網站過來的獨立訪客的數量,在關係型數據庫中,咱們可能須要下面這樣的SQL查詢語句:
咱們能夠在NoSQL中創建以下的數據模型:
Counting Unique Users using Composite Keys
這樣,咱們就能夠把數據按UserID來排序,咱們就能夠很容易把同一個用戶的數據(一個用戶並不會產生太多的event)進行處理,去掉那些重複的站點(使用hash table或是別的什麼)。另外一個可選的技術是,咱們能夠對每個用戶創建一個數據實體,而後把其站點來源追加到這個數據實體中,固然,這樣一來,數據的更新在性能相比之下會有必定損失。
適用性: Ordered Key-Value Store 排序鍵值對數據庫, BigTable風格的數據庫。
這個技術更多的是數據處理技術,而不是數據建模技術。儘管如此,這個技術仍是會影響數據模型。這個技術最主要的想法是使用一個索引來找到知足某條件的數據,可是把數據聚合起須要使用全文搜索。仍是讓咱們來講一個示例。仍是用上面那個例子,咱們有不少的日誌,其中包括互聯網用戶和他們的訪問來源。讓咱們假定每條記錄都有一個UserID,還有用戶的種類 (Men,Women,Bloggers,等),以及用戶所在的城市,和訪問過的站點。咱們要乾的事是,爲每一個用戶種類找到知足某些條件(訪問源,所在城市,等)的的獨立用戶。
很明顯,咱們須要搜索那些知足條件的用戶,若是咱們使用反轉搜索,這會讓咱們把這事幹得很容易,如: {Category -> [user IDs]} 或 {Site -> [user IDs]}。使用這樣的索引,咱們能夠取兩個或多個UserID要的交集或並集(這個事很容易幹,並且能夠乾得很快,若是這些UserID是排好序的)。可是,咱們要按用戶種類來生成報表會變得有點麻煩,由於咱們用語句可能會像下面這樣
但這樣的SQL很沒有效率,由於category數據太多了。爲了應對這個問題,咱們能夠創建一個直接索引 {UserID -> [Categories]}而後咱們用它來生成報表:
Counting Unique Users using Inverse and Direct Indexes
最後,咱們須要明白,對每一個UserID的隨機查詢是很沒有效率的。咱們能夠經過批查詢處理來解決這個問題。這意味着,對於一些用戶集,咱們能夠進行預處理(不一樣的查詢條件)。
適用性: Key-Value Store鍵值對數據庫,Document Databases文檔數據庫,BigTable風格的數據庫。
樹形或是任意的圖(需反規格化)能夠被直接打成一條記錄或文檔存放。
Tree Aggregation
適用性:Key-Value鍵值對數據庫,Document Databases文檔數據庫
Adjacency Lists鄰接列表是一種圖–每個結點都是一個獨立的記錄,其包含了全部的父結點或子結點。這樣,咱們就能夠經過給定的父或子結點來進行搜索。固然,咱們須要經過hop查詢遍歷圖。這個技術在廣度和深度查詢,以及獲得某個結點的子樹上沒有效率。
適用性:Key-Value鍵值對數據庫,Document Databases文檔數據庫
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搜索引擎
Nested sets嵌套集是樹形結構的標準技術。它被普遍地用在了關係性數據庫中,它徹底地適用於Key-Value鍵值對數據庫和Document Databases文檔數據庫。這個技術的想法是把葉子結點存儲成一個數組,並經過使用索引的開始和結束來映射每個非葉子結點到一個葉子結點集,就以下圖所示同樣:
Modeling of eCommerce Catalog using Nested Sets
這樣的數據結構對於immutable data不變的數據有很是不錯的效率,由於其點內存空間小,而且能夠很快地找出全部的葉子結點而不須要樹的遍歷。儘管如此,在插入和更新上須要很高的性能成本,由於新的葉子結點須要大規模地更新索引。
適用性:Key-Value Stores鍵值數據庫,Document Databases文檔數據庫
搜索引擎基本上來講和扁平文檔一同工做,如:每個文檔是一個扁平的字段和值的例表。這種數據模型的用來把業務實體映射到一個文本文檔上,若是你的業務實體有很複雜的內部結構,這可能會變得頗有挑戰。一個典型的挑戰是把一個有層級的文檔映映射出來。例如,文檔中嵌套另外一個文檔。讓咱們看看下面的示例:
Nested Documents Problem
上面的每個業務實體代碼一種簡歷。其包括了人名和一個技能列表。我把這個層級文檔映射成一個文本文檔,一種方法是建立Skill和Level字段。這個模型能夠經過技術或是等級來搜索一我的,而上圖標註的那樣的組合查詢則會失敗。(陳皓注:由於分不清Excellent是不是Math仍是Poetry上的)
在引用中的[4.6]給出了一種解決方案。其爲每一個字段都標上數字 Skill_i 和 Level_i,這樣就能夠分開搜索每個對(下圖中使用了OR來遍歷查找全部可能的字段):
Nested Document Modeling using Numbered Field Names
這樣的方式根本沒有擴展性,對於一些複雜的問題來講只會讓代碼複雜度和維護工做變大。
適用性:Search Engines全文搜索
在附錄[4.6]中給出了這個技術用來解決扁平層次文檔。它用鄰近的查詢來限制可被查詢的單詞的範圍。下圖中,全部的技能和等級被放在一個字段中,叫 SkillAndLevel,查詢中出現的「Excellent」和「Poetry」必需一個緊跟另外一個:
Nested Document Modeling using Proximity Queries
附錄[4.3]中講述了這個技術被用在Solr中的一個成功案例。
適用性:Search Engines全文搜索
Graph databases圖數據庫,如neo4j是一個出衆的圖數據庫,尤爲是使用一個結點來探索鄰居結點,或是探索兩個或少許結點前的關係。可是處理大量的圖數據是很沒有效率的,由於圖數據庫的性能和擴展性並非其目的。分佈式的圖數據處理能夠被MapReduce 和 Message Passing pattern來處理。如:在我前一篇的文章中的那個示例。這個方法可讓Key-Value stores, Document databases和BigTable-style databases適合於處理大圖。