NoSQL 數(shù)據(jù)建模技術(shù)詳解
NoSQL 數(shù)據(jù)庫(kù)經(jīng)常被用作很多非功能性的地方,如,擴(kuò)展性,性能和一致性的地方。這些NoSQL的特性在理論和實(shí)踐中都正在被大眾廣泛地研究著,研究的熱點(diǎn)正是那些和性能分布式相關(guān)的非功能性的東西,我們都知道 CAP 理論被很好地應(yīng)用于了 NoSQL 系統(tǒng)中(陳皓注:CAP即,一致性(Consistency), 可用性(Availability), 分區(qū)容忍性(Partition tolerance),在分布式系統(tǒng)中,這三個(gè)要素最多只能同時(shí)實(shí)現(xiàn)兩個(gè),而NoSQL一般放棄的是一致性)。但在另一方面,NoSQL的數(shù)據(jù)建模技術(shù)卻因?yàn)槿狈ο耜P(guān)系型數(shù)據(jù)庫(kù)那樣的基礎(chǔ)理論沒(méi)有被世人很好地研究。這篇文章從數(shù)據(jù)建模方面對(duì)NoSQL家族進(jìn)行了比較,并討論幾個(gè)常見(jiàn)的數(shù)據(jù)建模技術(shù)。
要開(kāi)始討論數(shù)據(jù)建模技術(shù),我們不得不或多或少地先系統(tǒng)地看一下NoSQL數(shù)據(jù)模型的成長(zhǎng)的趨勢(shì),以此我們可以了解一些他們內(nèi)在的聯(lián)系。下圖是NoSQL家族的進(jìn)化圖,我們可以看到這樣的進(jìn)化:Key-Value時(shí)代,BigTable時(shí)代,Document時(shí)代,全文搜索時(shí)代,和Graph數(shù)據(jù)庫(kù)時(shí)代:(陳皓注:注意圖中SQL說(shuō)的那句話(huà),NoSQL再這樣發(fā)展下去就是SQL了,哈哈。)
NoSQL Data Models#p#
首先,我們需要注意的是SQL和關(guān)系型數(shù)據(jù)模型已存在了很長(zhǎng)的時(shí)間,這種面向用戶(hù)的自然性意味著:
- 最終用戶(hù)一般更感興趣于數(shù)據(jù)的聚合顯示,而不是分離的數(shù)據(jù),這主要通過(guò)SQL來(lái)完成。
- 我們無(wú)法通過(guò)人手工控制數(shù)據(jù)的并發(fā)性,完整性,一致性,或是數(shù)據(jù)類(lèi)型校驗(yàn)這些東西的。這就是為什么SQL需要在事務(wù),二維表結(jié)構(gòu)(schema)和外表聯(lián)合上做很多事。
另一方面,SQL可以讓軟件應(yīng)用程序在很多情況下不需要關(guān)心數(shù)據(jù)庫(kù)的數(shù)據(jù)聚合,和數(shù)據(jù)完整性和有效性進(jìn)行控制。而如果我們?nèi)コ藬?shù)據(jù)一致性,完整性這些東西,會(huì)對(duì)性能和分布存儲(chǔ)有著重的幫助。正因?yàn)槿绱耍覀儾庞袛?shù)據(jù)模型的進(jìn)化:
- Key-Value 鍵值對(duì)存儲(chǔ)是非常簡(jiǎn)單而強(qiáng)大的。下面的很多技術(shù)基本上都是基于這個(gè)技術(shù)開(kāi)始發(fā)展的。但是,Key-Value有一個(gè)非常致命的問(wèn)題,那就是如果我們需要查找一段范圍內(nèi)的key。(陳皓注:學(xué)過(guò)hash-table數(shù)據(jù)結(jié)構(gòu)的人都應(yīng)該知道,hash-table是非序列容器,其并不像數(shù)組,鏈接,隊(duì)列這些有序容器,我們可以控制數(shù)據(jù)存儲(chǔ)的順序)。于是,有序鍵值 (Ordered Key-Value) 數(shù)據(jù)模型被設(shè)計(jì)出來(lái)解決這一限制,來(lái)從根本上提高數(shù)據(jù)集的問(wèn)題。
- Ordered Key-Value 有序鍵值模型也非常強(qiáng)大,但是,其也沒(méi)有對(duì)Value提供某種數(shù)據(jù)模型。通常來(lái)說(shuō),Value的模型可以由應(yīng)用負(fù)責(zé)解析和存取。這種很不方便,于是出現(xiàn)了 BigTable類(lèi)型的數(shù)據(jù)庫(kù),這個(gè)數(shù)據(jù)模型其實(shí)就是map里有map,map里再套map,一層一層套下去,也就是層層嵌套的key-value(value里又是一個(gè)key-value),這種數(shù)據(jù)庫(kù)的Value主要通過(guò)“列族”(column families),列,和時(shí)間戳來(lái)控制版本。(陳皓注:關(guān)于時(shí)間戳來(lái)對(duì)數(shù)據(jù)的版本控制主要是解決數(shù)據(jù)存儲(chǔ)并發(fā)問(wèn)題,也就是所謂的樂(lè)觀鎖,詳見(jiàn)《多版本并發(fā)控制(MVCC)在分布式系統(tǒng)中的應(yīng)用》)
- Document databases 文檔數(shù)據(jù)庫(kù) 改進(jìn)了 BigTable 模型,并提供了兩個(gè)有意義的改善。***個(gè)是允許Value中有主觀的模式(scheme),而不是map套map。第二個(gè)是索引。 Full Text Search Engines 全文搜索引擎可以被看作是文檔數(shù)據(jù)庫(kù)的一個(gè)變種,他們可以提供靈活的可變的數(shù)據(jù)模式(scheme)以及自動(dòng)索引。他們之間的不同點(diǎn)主要是,文檔數(shù)據(jù)庫(kù)用字段名做索引,而全文搜索引擎用字段值做索引。
- Graph data models 圖式數(shù)據(jù)庫(kù) 可以被認(rèn)為是這個(gè)進(jìn)化過(guò)程中從 Ordered Key-Value 數(shù)據(jù)庫(kù)發(fā)展過(guò)來(lái)的一個(gè)分支。圖式數(shù)據(jù)庫(kù)允許構(gòu)建議圖結(jié)構(gòu)的數(shù)據(jù)模型。它和文檔數(shù)據(jù)庫(kù)有關(guān)系的原因是,它的很多實(shí)現(xiàn)允許value可以是一個(gè)map或是一個(gè)document。#p#
NoSQL 數(shù)據(jù)模型摘要
本文剩下的章節(jié)將向你介紹數(shù)據(jù)建模的技術(shù)實(shí)現(xiàn)和相關(guān)模式。但是,在介紹這些技術(shù)之前,先來(lái)一段序言:
- NoSQL 數(shù)據(jù)模型設(shè)計(jì)一般從業(yè)務(wù)應(yīng)用的具體數(shù)據(jù)查詢(xún)?nèi)胧?,而不是?shù)據(jù)間的關(guān)系:
- 關(guān)系型的數(shù)據(jù)模型基本上是分析數(shù)據(jù)間的結(jié)構(gòu)和關(guān)系。其設(shè)計(jì)理念是: ”What answers do I have?”
- NoSQL 數(shù)據(jù)模型基本上是從應(yīng)用對(duì)數(shù)據(jù)的存取方式入手,如:我需要支持某種數(shù)據(jù)查詢(xún)。其設(shè)計(jì)理念是 ”What questions do I have?”
- NoSQL 數(shù)據(jù)模型設(shè)計(jì)比關(guān)系型數(shù)據(jù)庫(kù)需要對(duì)數(shù)據(jù)結(jié)構(gòu)和算法的更深的了解。在這篇文章中我會(huì)和大家說(shuō)那些盡人皆知的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)并不只是被NoSQL使用,但是對(duì)于NoSQL的數(shù)據(jù)模型卻非常有幫助。
- 數(shù)據(jù)冗余和反規(guī)格化是一等公民。
- 關(guān)系型數(shù)據(jù)庫(kù)對(duì)于處理層級(jí)數(shù)據(jù)和圖式數(shù)據(jù)非常的不方便。NoSQL用來(lái)解決圖式數(shù)據(jù)明顯是一個(gè)非常好的解決方案,幾乎所有的NoSQL數(shù)據(jù)庫(kù)可以很強(qiáng)地解決此類(lèi)問(wèn)題。這就是為什么這篇文章專(zhuān)門(mén)拿出一章來(lái)說(shuō)明層級(jí)數(shù)據(jù)模型。
- Key-Value 存儲(chǔ): Oracle Coherence, Redis, Kyoto Cabinet
- 類(lèi)BigTable存儲(chǔ): Apache HBase, Apache Cassandra
- 文檔數(shù)據(jù)庫(kù): MongoDB, CouchDB
- 全文索引: Apache Lucene, Apache Solr
- 圖數(shù)據(jù)庫(kù): neo4j, FlockDB
概念技術(shù) Conceptual Techniques
這一節(jié)主要介紹NoSQL數(shù)據(jù)模型的基本原則。
(1) 反規(guī)格化 Denormalization
反規(guī)格化 Denormalization 可以被認(rèn)為是把相同的數(shù)據(jù)拷貝到不同的文檔或是表中,這樣就可以簡(jiǎn)化和優(yōu)化查詢(xún),或是正好適合用戶(hù)的某中特別的數(shù)據(jù)模型。這篇文章中所說(shuō)的絕大多數(shù)技術(shù)都或多或少地導(dǎo)向了這一技術(shù)。
總體來(lái)說(shuō),反規(guī)格化需要權(quán)衡下面這些東西:
- 查詢(xún)數(shù)據(jù)量 /查詢(xún)IO VS 總數(shù)據(jù)量。使用反規(guī)格化,一方面可以把一條查詢(xún)語(yǔ)句所需要的所有數(shù)據(jù)組合起來(lái)放到一個(gè)地方存儲(chǔ)。這意味著,其它不同不同查詢(xún)所需要的相同的數(shù)據(jù),需要放在別不同的地方。因此,這產(chǎn)生了很多冗余的數(shù)據(jù),從而導(dǎo)致了數(shù)據(jù)量的增大。
- 處理復(fù)雜度 VS 總數(shù)據(jù)量. 在符合范式的數(shù)據(jù)模式上進(jìn)行表連接的查詢(xún),很顯然會(huì)增加了查詢(xún)處理的復(fù)雜度,尤其對(duì)于分布式系統(tǒng)來(lái)說(shuō)更是。反規(guī)格化的數(shù)據(jù)模型允許我們以方便查詢(xún)的方式來(lái)存構(gòu)造數(shù)據(jù)結(jié)構(gòu)以簡(jiǎn)化查詢(xún)復(fù)雜度。
適用性: Key-Value Store 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù)。#p#
(2) 聚合 Aggregates
所有類(lèi)型的NoSQL數(shù)據(jù)庫(kù)都會(huì)提供靈活的Schema(數(shù)據(jù)結(jié)構(gòu),對(duì)數(shù)據(jù)格式的限制):
- Key-Value Stores 和 Graph Databases 基本上來(lái)說(shuō)不會(huì)Value的形式,所以Value可以是任意格式。這樣一來(lái),這使得我們可以任意組合一個(gè)業(yè)務(wù)實(shí)體的keys。比如,我們有一個(gè)用戶(hù)帳號(hào)的業(yè)務(wù)實(shí)體,其可以被如下這些key組合起來(lái): UserID_name, UserID_email, UserID_messages 等等。如果一個(gè)用戶(hù)沒(méi)有email或message,那么相應(yīng)也不會(huì)有這樣的記錄。
- BigTable 模型通過(guò)列集合來(lái)支持靈活的Schema,我們稱(chēng)之為列族(column family)。BigTable還可以在同一記錄上出現(xiàn)不同的版本(通過(guò)時(shí)間戳)。
- Document databases 文檔數(shù)據(jù)庫(kù)是一種層級(jí)式的“去Schema”的存儲(chǔ),雖然有些這樣的數(shù)據(jù)庫(kù)允許檢驗(yàn)需要保存的數(shù)據(jù)是否滿(mǎn)足某種Schema。
靈活的Schema允許你可以用一種嵌套式的內(nèi)部數(shù)據(jù)方式來(lái)存儲(chǔ)一組有關(guān)聯(lián)的業(yè)務(wù)實(shí)體(陳皓注:類(lèi)似于JSON這樣的數(shù)據(jù)封裝格式)。這樣可以為我們帶來(lái)兩個(gè)好處。
- 最小化“一對(duì)多”關(guān)系——可以通過(guò)嵌套式的方式來(lái)存儲(chǔ)實(shí)體,這樣可以少一些表聯(lián)結(jié)。
- 可以讓內(nèi)部技術(shù)上的數(shù)據(jù)存儲(chǔ)更接近于業(yè)務(wù)實(shí)體,特別是那種混合式的業(yè)務(wù)實(shí)體。可能存于一個(gè)文檔集或是一張表中。
- 首先,所有的商品Product都會(huì)有一個(gè)ID,Price 和 Description。
- 然后,我們可以知道不同的類(lèi)型的商品會(huì)有不同的屬性。比如,作者是書(shū)的屬性,長(zhǎng)度是牛仔褲的屬性。其些屬性可能是“一對(duì)多”或是“多對(duì)多”的關(guān)系,如:唱片中的曲目。
- 接下來(lái),我們知道,某些業(yè)務(wù)實(shí)體不可能使用固定的類(lèi)型。如:牛仔褲的屬性并不是所有的牌子都有的,而且,有些名牌還會(huì)搞非常特別的屬性。
對(duì)于關(guān)系型數(shù)據(jù)庫(kù)來(lái)說(shuō),要設(shè)計(jì)這樣的數(shù)據(jù)模型并不簡(jiǎn)單,而且設(shè)計(jì)出來(lái)的絕對(duì)離優(yōu)雅很遠(yuǎn)很遠(yuǎn)。而我們NoSQL中靈活的Schema允許你使用一個(gè)聚合 Aggregate (product) 可以建出所有不同種類(lèi)的商品和他們的不同的屬性:
Entity Aggregation
上圖中我們可以比較關(guān)系型數(shù)據(jù)庫(kù)和NoSQL的差別。但是我們可以看到在數(shù)據(jù)更新上,非規(guī)格化的數(shù)據(jù)存儲(chǔ)在性能和一致性上會(huì)有很大的影響,這就是我們需要重點(diǎn)注意和不得不犧牲的地方。
適用性: Key-Value Store 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù)。#p#
(3) 應(yīng)用層聯(lián)結(jié) Application Side Joins
表聯(lián)結(jié)基本上不被NoSQL支持。正如我們前面所說(shuō)的,NoSQL是“面向問(wèn)題”而不是“面向答案”的,不支持表聯(lián)結(jié)就是“面向問(wèn)題”的后果。表的聯(lián)結(jié)是在設(shè)計(jì)時(shí)被構(gòu)造出來(lái)的,而不是在執(zhí)行時(shí)建造出來(lái)的。所以,表聯(lián)結(jié)在運(yùn)行時(shí)是有很大開(kāi)銷(xiāo)的(陳皓注:搞過(guò)SQL表聯(lián)結(jié)的都知道笛卡爾積是什么東西,大可以在參看以前酷殼的“圖解數(shù)據(jù)庫(kù)表Joins”),但是在使用了 Denormalization 和 Aggregates 技術(shù)后,我們基本不用進(jìn)行表聯(lián)結(jié),如:你們使用嵌套式的數(shù)據(jù)實(shí)體。當(dāng)然,如果你需要聯(lián)結(jié)數(shù)據(jù),你需要在應(yīng)用層完成這個(gè)事。下面是幾個(gè)主要的Use Case:
- 多對(duì)多的數(shù)據(jù)實(shí)體關(guān)系——經(jīng)常需要被連接或聯(lián)結(jié)。
- 聚合 Aggregates 并不適用于數(shù)據(jù)字段經(jīng)常被改變的情況。對(duì)此,我們需要把那些經(jīng)常被改變的字段分到另外的表中,而在查詢(xún)時(shí)我們需要聯(lián)結(jié)數(shù)據(jù)。例如,我們有個(gè)Message系統(tǒng)可以有一個(gè)User實(shí)體,其包括了一個(gè)內(nèi)嵌的Message實(shí)體。但是,如果用戶(hù)不斷在附加 message,那么,***把message拆分到另一個(gè)獨(dú)立的實(shí)體,但在查詢(xún)時(shí)聯(lián)結(jié)這User和Message這兩個(gè)實(shí)體。如下圖:
適用性: Key-Value Store 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù), Graph Databases 圖數(shù)據(jù)庫(kù)。
通用建模技術(shù) General Modeling Techniques
在本書(shū)中,我們將討論NoSQL中各種不同的通用的數(shù)據(jù)建模技術(shù)。#p#
(4) 原子聚合 Atomic Aggregates
很多NoSQL的數(shù)據(jù)庫(kù)(并不是所有)在事務(wù)處理上都是短板。在某些情況下,他們可以通過(guò)分布式鎖技術(shù)或是應(yīng)用層管理的MVCC技術(shù)來(lái)實(shí)現(xiàn)其事務(wù)性(陳皓注:可參看本站的“多版本并發(fā)控制(MVCC)在分布式系統(tǒng)中的應(yīng)用”)但是,通常來(lái)說(shuō)只能使用聚合Aggregates技術(shù)來(lái)保證一些ACID原則。
這就是為什么我們的關(guān)系型數(shù)據(jù)庫(kù)需要有強(qiáng)大的事務(wù)處理機(jī)制——因?yàn)殛P(guān)系型數(shù)據(jù)庫(kù)的數(shù)據(jù)是被規(guī)格化存放在了不同的地方。所以,Aggregates聚合允許我們把一個(gè)業(yè)務(wù)實(shí)體存成一個(gè)文檔、存成一行,存成一個(gè)key-value,這樣就可以原子式的更新了:
Atomic Aggregates
當(dāng)然,原子聚合 Atomic Aggregates 這種數(shù)據(jù)模型并不能實(shí)現(xiàn)完全意義上的事務(wù)處理,但是如果支持原子性,鎖,或 test-and-set 指令,那么, Atomic Aggregates 是可以適用的。
適用性: Key-Value Store 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù)。
(5) 可枚舉鍵 Enumerable Keys
也許,對(duì)于無(wú)順序的Key-Value***的好處是業(yè)務(wù)實(shí)體可以被容易地hash以分區(qū)在多個(gè)服務(wù)器上。而排序了的key會(huì)把事情搞復(fù)雜,但是有些時(shí)候,一個(gè)應(yīng)用能從排序key中獲得很多好處,就算是數(shù)據(jù)庫(kù)本身不提供這個(gè)功能。讓我們來(lái)思考下email消息的數(shù)據(jù)模型:
- 一些NoSQL的數(shù)據(jù)庫(kù)提供原子計(jì)數(shù)器以允許生一些連續(xù)的ID。在這種情況下,我們可以使用 userID_messageID 來(lái)做為一個(gè)組合key。如果我們知道***的message ID,就可以知道前一個(gè)message,也可能知道再前面和后面的Message。
- Messages可以被打包。比如,每天的郵件包。這樣,我們就可以對(duì)郵件按指定的時(shí)間段來(lái)遍歷。
適用性: Key-Value Store 鍵值對(duì)數(shù)據(jù)庫(kù)。#p#
(6) 降維 Dimensionality Reduction
Dimensionality Reduction 降維是一種技術(shù)可以允許把一個(gè)多維的數(shù)據(jù)映射成一個(gè)Key-Value或是其它非多給的數(shù)據(jù)模型。
傳統(tǒng)的地理位置信息系統(tǒng)使用一些如“四分樹(shù)QuadTree” 或 “R-Tree” 來(lái)做地理位置索引。這些數(shù)據(jù)結(jié)構(gòu)的內(nèi)容需要被在適當(dāng)?shù)奈恢酶?,并且,如果?shù)據(jù)量很大的話(huà),操作成本會(huì)很高。另一個(gè)方法是我們可以遍歷一個(gè)二維的數(shù)據(jù)結(jié)構(gòu)并把其扁平化成一個(gè)列表。一個(gè)眾所周知的例子是Geohash(地理哈希)。一個(gè)Geohash使用“之字形”的路線(xiàn)掃描一個(gè)2維的空間,而且遍歷中的移動(dòng)可以被簡(jiǎn)單地用0和1來(lái)表示其方向,然后在移動(dòng)的過(guò)程中產(chǎn)生0/1串。下圖展示了這一算法:(陳皓注:先把地圖分成四份,經(jīng)度為***位,緯度為第二位,于是左邊的經(jīng)度是0,右邊的是1,緯度也一樣,上面是為1,下面的為0,這樣,經(jīng)緯度就可以組合成01,11,00,10這四個(gè)值,其標(biāo)識(shí)了四塊區(qū)域,我們可以如此不斷的遞歸地對(duì)每個(gè)區(qū)域進(jìn)行四分,然后可以得到一串1和0組成的字串,然后使用0-9,b-z 去掉(去掉a, i, l, o)這32個(gè)字母進(jìn)行base32編碼得到一個(gè)8個(gè)長(zhǎng)度的編碼,這就是Geohash的算法)
Geohash Index
Geohash的***大的功能是使用簡(jiǎn)單的位操作就可以知道兩個(gè)區(qū)域間的距離,就像圖中所示(陳皓:proximity框著的那兩個(gè),這個(gè)很像IP地址了)。Geohash把一個(gè)二維的坐標(biāo)生生地變成了一個(gè)一維的數(shù)據(jù)模型,這就是降維技術(shù)。BigTable的降維技術(shù)參看到文章后面的 [6.1]。更多的關(guān)于Geohash和其它技術(shù)可以參看 [6.2] 和 [6.3]。
適用性: Key-Value Store 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù)。#p#
(7) 索引表 Index Table
Index Table 索引表是一個(gè)非常直白的技術(shù),其可以你在不支持索引的數(shù)據(jù)庫(kù)中得到索引的好處。BigTable是這類(lèi)最重要的數(shù)據(jù)庫(kù)。這需要我們維護(hù)一個(gè)有相應(yīng)存取模式的特別表。例如,我們有一個(gè)主表存著用戶(hù)帳號(hào),其可以被UserID存取。某查詢(xún)需要查出某個(gè)城市里所有的用戶(hù),于是我們可以加入一張表,這張表用城市做主鍵,所有和這個(gè)城市相關(guān)的UserID是其Value,如下所示:
Index Table Example
可見(jiàn),城市索引表的需要和對(duì)主表用戶(hù)表保持一致性,因此,主表的每一個(gè)更新可能需要對(duì)索引表進(jìn)行更新,不然就是一個(gè)批處理更新。無(wú)論哪個(gè)方式,這都會(huì)損傷一些性能,因?yàn)樾枰3忠恢滦浴?/p>
Index Table 索引表可以被認(rèn)為是關(guān)系型數(shù)據(jù)庫(kù)中的視圖的等價(jià)物。
適用性: BigTable 數(shù)據(jù)庫(kù)。
(8) 鍵組合索引 Composite Key Index
Composite key 鍵組合是一個(gè)很常用的技術(shù),對(duì)此,當(dāng)我們的數(shù)據(jù)庫(kù)支持鍵排序時(shí)能得到極大的好處。Composite key組合鍵的拼接成為第二排序字段可以讓你構(gòu)建出一種多維索引,這很像我們之前說(shuō)過(guò)的 Dimensionality Reduction 降維技術(shù)。例如,我們需要存取用戶(hù)統(tǒng)計(jì)。如果我們需要根據(jù)不同的地區(qū)來(lái)統(tǒng)計(jì)用戶(hù)的分布情況,我們可以把Key設(shè)計(jì)成這樣的格式 (State:City:UserID),這樣一來(lái),就使得我們可以通過(guò)State到City來(lái)按組遍歷用戶(hù),特別是我們的NoSQL數(shù)據(jù)庫(kù)支持在key上按區(qū)查詢(xún)(如:BigTable類(lèi)的系統(tǒng)):
1
2
|
SELECT Values WHERE state="CA:*"
SELECT Values WHERE city="CA:San Francisco*"
|
Composite Key Index
適用性: BigTable 數(shù)據(jù)庫(kù)。#p#
(9) 鍵組合聚合 Aggregation with Composite Keys
Composite keys 鍵組合技術(shù)并不僅僅可以用來(lái)做索引,同樣可以用來(lái)區(qū)分不用的類(lèi)型的數(shù)據(jù)以支持?jǐn)?shù)據(jù)分組??紤]一個(gè)例子,我們有一個(gè)海量的日志數(shù)組,這個(gè)日志記錄了互聯(lián)網(wǎng)上的用戶(hù)的訪(fǎng)問(wèn)來(lái)源。我們需要計(jì)算從某一網(wǎng)站過(guò)來(lái)的獨(dú)立訪(fǎng)客的數(shù)量,在關(guān)系型數(shù)據(jù)庫(kù)中,我們可能需要下面這樣的SQL查詢(xún)語(yǔ)句:
1
|
SELECT count(distinct(user_id)) FROM clicks GROUP BY site
|
我們可以在NoSQL中建立如下的數(shù)據(jù)模型:
Counting Unique Users using Composite Keys
這樣,我們就可以把數(shù)據(jù)按UserID來(lái)排序,我們就可以很容易把同一個(gè)用戶(hù)的數(shù)據(jù)(一個(gè)用戶(hù)并不會(huì)產(chǎn)生太多的event)進(jìn)行處理,去掉那些重復(fù)的站點(diǎn)(使用hash table或是別的什么)。另一個(gè)可選的技術(shù)是,我們可以對(duì)每一個(gè)用戶(hù)建立一個(gè)數(shù)據(jù)實(shí)體,然后把其站點(diǎn)來(lái)源追加到這個(gè)數(shù)據(jù)實(shí)體中,當(dāng)然,這樣一來(lái),數(shù)據(jù)的更新在性能相比之下會(huì)有一定損失。
適用性: Ordered Key-Value Store 排序鍵值對(duì)數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù)。
(10) 反轉(zhuǎn)搜索 Inverted Search – 直接聚合 Direct Aggregation
這個(gè)技術(shù)更多的是數(shù)據(jù)處理技術(shù),而不是數(shù)據(jù)建模技術(shù)。盡管如此,這個(gè)技術(shù)還是會(huì)影響數(shù)據(jù)模型。這個(gè)技術(shù)最主要的想法是使用一個(gè)索引來(lái)找到滿(mǎn)足某條件的數(shù)據(jù),但是把數(shù)據(jù)聚合起需要使用全文搜索。還是讓我們來(lái)說(shuō)一個(gè)示例。還是用上面那個(gè)例子,我們有很多的日志,其中包括互聯(lián)網(wǎng)用戶(hù)和他們的訪(fǎng)問(wèn)來(lái)源。讓我們假定每條記錄都有一個(gè)UserID,還有用戶(hù)的種類(lèi) (Men, Women, Bloggers, 等),以及用戶(hù)所在的城市,和訪(fǎng)問(wèn)過(guò)的站點(diǎn)。我們要干的事是,為每個(gè)用戶(hù)種類(lèi)找到滿(mǎn)足某些條件(訪(fǎng)問(wèn)源,所在城市,等)的的獨(dú)立用戶(hù)。
很明顯,我們需要搜索那些滿(mǎn)足條件的用戶(hù),如果我們使用反轉(zhuǎn)搜索,這會(huì)讓我們把這事干得很容易,如: {Category -> [user IDs]} 或 {Site -> [user IDs]}。使用這樣的索引, 我們可以取兩個(gè)或多個(gè)UserID要的交集或并集(這個(gè)事很容易干,而且可以干得很快,如果這些UserID是排好序的)。但是,我們要按用戶(hù)種類(lèi)來(lái)生成報(bào)表會(huì)變得有點(diǎn)麻煩,因?yàn)槲覀冇谜Z(yǔ)句可能會(huì)像下面這樣
1
|
SELECT count(distinct(user_id)) ... GROUP BY category
|
但這樣的SQL很沒(méi)有效率,因?yàn)閏ategory數(shù)據(jù)太多了。為了應(yīng)對(duì)這個(gè)問(wèn)題,我們可以建立一個(gè)直接索引 {UserID -> [Categories]} 然后我們用它來(lái)生成報(bào)表:
Counting Unique Users using Inverse and Direct Indexes
***,我們需要明白,對(duì)每個(gè)UserID的隨機(jī)查詢(xún)是很沒(méi)有效率的。我們可以通過(guò)批查詢(xún)處理來(lái)解決這個(gè)問(wèn)題。這意味著,對(duì)于一些用戶(hù)集,我們可以進(jìn)行預(yù)處理(不同的查詢(xún)條件)。
適用性: Key-Value Store 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù)。#p#
層級(jí)式模型 Hierarchy Modeling Techniques
(11) 樹(shù)形聚合Tree Aggregation
樹(shù)形或是任意的圖(需反規(guī)格化)可以被直接打成一條記錄或文檔存放。
- 當(dāng)樹(shù)形結(jié)構(gòu)被一次性取出時(shí)這會(huì)非常有效率(如:我們需要展示一個(gè)blog的樹(shù)形評(píng)論)
- 搜索和任何存取這個(gè)實(shí)體都會(huì)存在問(wèn)題。
- 對(duì)于大多數(shù)NoSQL的實(shí)現(xiàn)來(lái)說(shuō),更新數(shù)據(jù)都是很不經(jīng)濟(jì)的(相比起獨(dú)立結(jié)點(diǎn)來(lái)說(shuō))
Tree Aggregation
適用性: Key-Value 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases 文檔數(shù)據(jù)庫(kù)
(12) 鄰接列表 Adjacency Lists
Adjacency Lists 鄰接列表是一種圖 – 每一個(gè)結(jié)點(diǎn)都是一個(gè)獨(dú)立的記錄,其包含了 所有的父結(jié)點(diǎn)或子結(jié)點(diǎn)。這樣,我們就可以通過(guò)給定的父或子結(jié)點(diǎn)來(lái)進(jìn)行搜索。當(dāng)然,我們需要通過(guò)hop查詢(xún)遍歷圖。這個(gè)技術(shù)在廣度和深度查詢(xún),以及得到某個(gè)結(jié)點(diǎn)的子樹(shù)上沒(méi)有效率。
適用性: Key-Value 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases 文檔數(shù)據(jù)庫(kù)#p#
(13) Materialized Paths
Materialized Paths 可以幫助避免遞歸遍歷(如:樹(shù)形結(jié)構(gòu))。這個(gè)技術(shù)也可以被認(rèn)為是反規(guī)格化的一種變種。其想法是為每個(gè)結(jié)點(diǎn)加上父結(jié)點(diǎn)或子結(jié)點(diǎn)的標(biāo)識(shí)屬性,這樣就可以不需要遍歷就知道所有的后裔結(jié)點(diǎn)和祖先結(jié)點(diǎn)了:
Materialized Paths for eShop Category Hierarchy
這個(gè)技術(shù)對(duì)于全文搜索引擎來(lái)說(shuō)非常有幫助,因?yàn)槠淇梢栽试S把一個(gè)層級(jí)結(jié)構(gòu)轉(zhuǎn)成一個(gè)文檔。上面的示圖中我們可以看到所有的商品或Men’s Shoes下的子分類(lèi)可以被一條很短的查詢(xún)語(yǔ)句處理——只需要給定個(gè)分類(lèi)名。
Materialized Paths 可以存儲(chǔ)一個(gè)ID的集合,或是一堆ID拼出的字符串。后者允許你通過(guò)一個(gè)正則表達(dá)式來(lái)搜索一個(gè)特定的分支路徑。下圖展示了這個(gè)技術(shù)(分支的路徑包括了結(jié)點(diǎn)本身):
Query Materialized Paths using RegExp
適用性: Key-Value 鍵值對(duì)數(shù)據(jù)庫(kù), Document Databases 文檔數(shù)據(jù), Search Engines 搜索引擎
(14) 嵌套集 Nested Sets
Nested sets 嵌套集是樹(shù)形結(jié)構(gòu)的標(biāo)準(zhǔn)技術(shù)。它被廣泛地用在了關(guān)系性數(shù)據(jù)庫(kù)中,它完全地適用于 Key-Value 鍵值對(duì)數(shù)據(jù)庫(kù) 和 Document Databases 文檔數(shù)據(jù)庫(kù)。這個(gè)技術(shù)的想法是把葉子結(jié)點(diǎn)存儲(chǔ)成一個(gè)數(shù)組,并通過(guò)使用索引的開(kāi)始和結(jié)束來(lái)映射每一個(gè)非葉子結(jié)點(diǎn)到一個(gè)葉子結(jié)點(diǎn)集,就如下圖所示一樣:
Modeling of eCommerce Catalog using Nested Sets
這樣的數(shù)據(jù)結(jié)構(gòu)對(duì)于immutable data不變的數(shù)據(jù) 有非常不錯(cuò)的效率,因?yàn)槠潼c(diǎn)內(nèi)存空間小,并且可以很快地找出所有的葉子結(jié)點(diǎn)而不需要樹(shù)的遍歷。盡管如此,在插入和更新上需要很高的性能成本,因?yàn)樾碌娜~子結(jié)點(diǎn)需要大規(guī)模地更新索引。
適用性: Key-Value Stores 鍵值數(shù)據(jù)庫(kù), Document Databases 文檔數(shù)據(jù)庫(kù)#p#
(15) 嵌套文檔扁平化:有限的字段名 Nested Documents Flattening: Numbered Field Names
搜索引擎基本上來(lái)說(shuō)和扁平文檔一同工作,如:每一個(gè)文檔是一個(gè)扁平的字段和值的例表。這種數(shù)據(jù)模型的用來(lái)把業(yè)務(wù)實(shí)體映射到一個(gè)文本文檔上,如果你的業(yè)務(wù)實(shí)體有很復(fù)雜的內(nèi)部結(jié)構(gòu),這可能會(huì)變得很有挑戰(zhàn)。一個(gè)典型的挑戰(zhàn)是把一個(gè)有層級(jí)的文檔映映射出來(lái)。例如,文檔中嵌套另一個(gè)文檔。讓我們看看下面的示例:
Nested Documents Problem
上面的每一個(gè)業(yè)務(wù)實(shí)體代碼一種簡(jiǎn)歷。其包括了人名和一個(gè)技能列表。我把這個(gè)層級(jí)文檔映射成一個(gè)文本文檔,一種方法是創(chuàng)建 Skill 和 Level 字段。這個(gè)模型可以通過(guò)技術(shù)或是等級(jí)來(lái)搜索一個(gè)人,而上圖標(biāo)注的那樣的組合查詢(xún)則會(huì)失敗。(陳皓注:因?yàn)榉植磺錏xcellent是否是Math還是Poetry上的)
在引用中的 [4.6] 給出了一種解決方案。其為每個(gè)字段都標(biāo)上數(shù)字 Skill_i 和 Level_i,這樣就可以分開(kāi)搜索每一個(gè)對(duì)(下圖中使用了OR來(lái)遍歷查找所有可能的字段):
Nested Document Modeling using Numbered Field Names
這樣的方式根本沒(méi)有擴(kuò)展性,對(duì)于一些復(fù)雜的問(wèn)題來(lái)說(shuō)只會(huì)讓代碼復(fù)雜度和維護(hù)工作變大。
適用性: Search Engines 全文搜索#p#
(16)嵌套文檔扁平化:鄰近查詢(xún) Nested Documents Flattening: Proximity Queries
在附錄 [4.6]中給出了這個(gè)技術(shù)用來(lái)解決扁平層次文檔。它用鄰近的查詢(xún)來(lái)限制可被查詢(xún)的單詞的范圍。下圖中,所有的技能和等級(jí)被放在一個(gè)字段中,叫 SkillAndLevel,查詢(xún)中出現(xiàn)的 “Excellent” 和 “Poetry” 必需一個(gè)緊跟另一個(gè):
Nested Document Modeling using Proximity Queries
附錄 [4.3] 中講述了這個(gè)技術(shù)被用在Solr中的一個(gè)成功案例。
適用性: Search Engines 全文搜索
(17) 圖結(jié)構(gòu)批處理 Batch Graph Processing
Graph databases 圖數(shù)據(jù)庫(kù),如 neo4j 是一個(gè)出眾的圖數(shù)據(jù)庫(kù),尤其是使用一個(gè)結(jié)點(diǎn)來(lái)探索鄰居結(jié)點(diǎn),或是探索兩個(gè)或少量結(jié)點(diǎn)前的關(guān)系。但是處理大量的圖數(shù)據(jù)是很沒(méi)有效率的,因?yàn)閳D數(shù)據(jù)庫(kù)的性能和擴(kuò)展性并不是其目的。分布式的圖數(shù)據(jù)處理可以被 MapReduce 和 Message Passing pattern 來(lái)處理。如: 在我前一篇的文章中的那個(gè)示例。這個(gè)方法可以讓 Key-Value stores, Document databases, 和 BigTable-style databases 適合于處理大圖。
Applicability: Key-Value Stores, Document Databases, BigTable-style Databases