從數(shù)據(jù)建模的角度對(duì)NoSQL家族系統(tǒng)做了比較簡(jiǎn)單的比較,并簡(jiǎn)要介紹幾種常見建模技術(shù)。
1.前言
為了適應(yīng)大數(shù)據(jù)應(yīng)用場(chǎng)景的要求,Hadoop以及NoSQL等與傳統(tǒng)企業(yè)平臺(tái)完全不同的新興架構(gòu)迅速地崛起。而下層技術(shù)基礎(chǔ)的革命必將影響上層建筑:數(shù)據(jù)模型和算法。簡(jiǎn)單地將傳統(tǒng)基于第四范式結(jié)構(gòu)化關(guān)系型數(shù)據(jù)庫(kù)的模型拷貝到新的引擎上,無(wú)異于削足適履,不僅增加了大數(shù)據(jù)應(yīng)用開發(fā)的難度和復(fù)雜度,又無(wú)法發(fā)釋放新框架的潛能。
該如何構(gòu)建基于NoSQL的數(shù)據(jù)模型?現(xiàn)在能供參考的公開知識(shí)積累要么是空虛簡(jiǎn)單的一句“去規(guī)范化“或粗暴的寬表化(將query和應(yīng)用需要訪問的所有字段“排排坐“,放在一個(gè)有很多列的結(jié)構(gòu)化表中),要么是針對(duì)具體工具或具體場(chǎng)景的實(shí)現(xiàn)細(xì)節(jié),(如《HBase權(quán)威指南》中對(duì)于如何設(shè)計(jì)HBase主鍵的探討)。沒有一個(gè)像編程的設(shè)計(jì)模式一樣的,在模型架構(gòu)層面可以遵循的方法論。
在比較不同的NoSQL數(shù)據(jù)庫(kù)時(shí),通常使用功能以外其他各種指標(biāo),如可擴(kuò)展性、性能和一致性。由于這些指標(biāo)通常是使用NoSQL的初衷,所以無(wú)論從理論的角度還是實(shí)踐的角度被深入地研究了,而像CAP定理這樣的分布式系統(tǒng)基礎(chǔ)結(jié)論也同樣適用于NoSQL系統(tǒng)。另一方面,在NoSQL的數(shù)據(jù)模型領(lǐng)域,卻還沒有很好地研究過(guò),也缺乏關(guān)系數(shù)據(jù)庫(kù)中那種系統(tǒng)性的理論。
我在這篇文章中從數(shù)據(jù)建模的角度對(duì)NoSQL家族系統(tǒng)做了比較簡(jiǎn)單的比較,并簡(jiǎn)要介紹幾種常見建模技術(shù)。
2.NoSQL數(shù)據(jù)模型視圖
要探索數(shù)據(jù)建模技術(shù),必須先從系統(tǒng)性的NoSQL數(shù)據(jù)模型視圖著手,這多多少少能幫助我們揭示其發(fā)展趨勢(shì)以及相互之間的關(guān)系。下圖描繪了主要NoSQL家族系統(tǒng)的虛擬“進(jìn)化”過(guò)程,即鍵值存儲(chǔ),BigTable類型的數(shù)據(jù)庫(kù),文檔數(shù)據(jù)庫(kù),全文搜索引擎,數(shù)據(jù)庫(kù)和圖形數(shù)據(jù)庫(kù):
首先,我們應(yīng)該注意到,一般意義上講,SQL和關(guān)系型模型都是在很久以前就被設(shè)計(jì)出來(lái),目的是為最終用戶交互之用。這種面向用戶的性質(zhì)有極深的影響:
最終用戶往往對(duì)匯總報(bào)表信息感興趣而不是單獨(dú)的數(shù)據(jù)項(xiàng),因而SQL這方面做了大量的工作。
不能指望作為自然人的用戶能顯式地控制并發(fā)性、完整性、一致性或者數(shù)據(jù)類型有效性。這就是為什么SQL竭力關(guān)注于事務(wù)保證、schema和參照完整性。
另一方面,軟件應(yīng)用程序往往對(duì)在數(shù)據(jù)庫(kù)內(nèi)部做聚合沒有太大的興趣,而且至少在許多情況下,程序能夠自己控制完整性和有效性。除此之外,剔除這些功能對(duì)于性能和可擴(kuò)展性存儲(chǔ)的影響極其重要。
新數(shù)據(jù)模型的演變開始了:
鍵-值存儲(chǔ)是一個(gè)非常簡(jiǎn)單,但非常強(qiáng)大的模型。下面所描述的許多技術(shù)都完全適用于這個(gè)模型。
鍵值模型最致命的缺點(diǎn)之一就是不適合按范圍處理主鍵的場(chǎng)景。有序的鍵-值模型突破了這一限制,并顯著提高了聚合能力。
有序的鍵-值模型非常強(qiáng)大,但它不提供任何針對(duì)值(value)的建??蚣?。在一般情況下,值的建模可以由應(yīng)用程序完成,但BigTable風(fēng)格的數(shù)據(jù)庫(kù)想得更加周到,它可以將值按照映射的映射的映射(map-of-maps-of-maps)進(jìn)行建模,說(shuō)得明確點(diǎn),分別是列簇(column family)、列(column)和時(shí)間戳化的版本。
文檔數(shù)據(jù)庫(kù)對(duì)BigTable模式提出兩個(gè)明顯的改善。第一,值可以被聲明為任意復(fù)雜的schema,而不僅僅是一個(gè)映射的映射(map-of-maps)。第二,至少有一些產(chǎn)品實(shí)現(xiàn)了被數(shù)據(jù)庫(kù)管理的索引。就這個(gè)意義上來(lái)講,全文搜索引擎也可以同樣被認(rèn)為提供了靈活的schema和自動(dòng)化的索引。他們之間主要區(qū)別在于,文檔數(shù)據(jù)庫(kù)是根據(jù)字段名對(duì)索引進(jìn)行編組,而搜索引擎是使用字段值對(duì)索引編組。值得注意的是像Oracle Coherence這樣的鍵-值存儲(chǔ)系統(tǒng)增加了索引和內(nèi)嵌入口處理器的功能,正逐步向文件數(shù)據(jù)庫(kù)演進(jìn)。
最后,圖形數(shù)據(jù)模型可以被視為有序的鍵-值模型朝另外一個(gè)方向的進(jìn)化。圖形數(shù)據(jù)庫(kù)允許對(duì)業(yè)務(wù)實(shí)體進(jìn)行非常透明的建模(這個(gè)東西取決于那個(gè)東西),而分層建模技術(shù)在這方面用的是另外的數(shù)據(jù)模型,但也可與之媲美。圖形數(shù)據(jù)庫(kù)和文件數(shù)據(jù)庫(kù)息息相關(guān),因?yàn)樵S多實(shí)現(xiàn)允許建模的值是映射或者文檔。
3.NoSQL數(shù)據(jù)建模的一般注意事項(xiàng)
與關(guān)系型建模不同,NoSQL數(shù)據(jù)建模往往是從特定查詢的應(yīng)用開始:
關(guān)系型建模是典型地被手上可用數(shù)據(jù)的結(jié)構(gòu)所驅(qū)動(dòng)。設(shè)計(jì)主要圍繞著的是“我有什么樣的答案?”
NoSQL數(shù)據(jù)建模通常由特定應(yīng)用的訪問模式所驅(qū)動(dòng),比如需要支持的查詢類型。設(shè)計(jì)主要圍繞著的是“我有什么問題?”
NoSQL數(shù)據(jù)建模往往比關(guān)系數(shù)據(jù)庫(kù)建模需要更加深入地了解數(shù)據(jù)結(jié)構(gòu)和算法。在這篇文章中,我介紹了幾個(gè)著名的數(shù)據(jù)結(jié)構(gòu),他們雖然非NoSQL所特有,但對(duì)于實(shí)際的NoSQL建模非常有用。
數(shù)據(jù)復(fù)制和去規(guī)范化是一等公民。
關(guān)系數(shù)據(jù)庫(kù)在對(duì)分層或圖形數(shù)據(jù)進(jìn)行建模和處理時(shí)不是很方便。圖形數(shù)據(jù)庫(kù)顯然是這個(gè)領(lǐng)域的完美解決方案,但實(shí)際上大多數(shù)的NoSQL也都非常善于解決這樣的問題。這就是為什么這篇文章為分層數(shù)據(jù)建模單獨(dú)寫了一個(gè)章節(jié)。
雖然數(shù)據(jù)建模技術(shù)基本上和具體實(shí)現(xiàn)無(wú)關(guān),但我還是列出了在寫這篇文章時(shí)我能想到的產(chǎn)品:
鍵值存儲(chǔ):Oracle Coherence,Redis,Kyoto Cabinet
BigTable風(fēng)格的數(shù)據(jù)庫(kù): Apache HBase,Apache Cassandra
文檔數(shù)據(jù)庫(kù): MongoDB,CouchDB
全文搜索引擎: Apache Lucene,Apache Solr
圖形數(shù)據(jù)庫(kù):Neo4j,F(xiàn)lockDB
4.概念技術(shù)
本節(jié)專門介紹NoSQL數(shù)據(jù)建模的基本原則。
1、 去規(guī)范化(Denormalization)
可以將去規(guī)范化定義為把相同的數(shù)據(jù)復(fù)制到多個(gè)文檔或數(shù)據(jù)表中,這樣可以簡(jiǎn)化/優(yōu)化查詢處理,或者讓用戶數(shù)據(jù)能匹配一個(gè)特定的數(shù)據(jù)模型。在本文的大多數(shù)技術(shù)用到了這樣或那樣的去規(guī)范化。
一般來(lái)說(shuō),去規(guī)范化用于以下的折衷:
查詢的數(shù)據(jù)量或每次查詢IO**與總數(shù)據(jù)量的折衷。去規(guī)范化可以將一個(gè)查詢所需的所有數(shù)據(jù)組合起來(lái)存放到同一個(gè)地方。這通常意味著對(duì)相同數(shù)據(jù)的不同的查詢會(huì)訪問不同的數(shù)據(jù)組合。因此,數(shù)據(jù)需要被復(fù)制多份,也就意味著增加了總數(shù)據(jù)量。
處理復(fù)雜性與總數(shù)據(jù)量的折衷。建模時(shí)的規(guī)范化和相應(yīng)查詢的連接(join)明顯增加了查詢處理器的復(fù)雜度,在分布式系統(tǒng)中尤為明顯。去規(guī)范化允許將數(shù)據(jù)按照查詢友好的方式存儲(chǔ),從而簡(jiǎn)化查詢的處理。
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù)
2、 聚合(Aggregates)
所有主流NoSQL都提供了這樣或那樣的松散schema(soft schema)支持:
鍵值存儲(chǔ)和圖形數(shù)據(jù)庫(kù)通常不對(duì)值進(jìn)行約束,所以值可能是任意格式。另外,也可以通過(guò)使用組合鍵將一個(gè)業(yè)務(wù)實(shí)體表示為多條記錄。例如,可以將一個(gè)用戶帳戶建模為UserID_name,UserID_email,UserID_messages等組合鍵表示的一個(gè)實(shí)體集合。如果用戶沒有電子郵件或消息,然后相應(yīng)的實(shí)體不會(huì)被記錄。
BigTable模式也支持松散schema,因?yàn)橐粋€(gè)列簇是可變的列集合,一個(gè)單元格又能存儲(chǔ)不定數(shù)目的數(shù)據(jù)版本。
文檔數(shù)據(jù)庫(kù)天生就沒schema,雖然某些文檔數(shù)據(jù)庫(kù)允許在數(shù)據(jù)輸入時(shí)使用用戶定義的schema進(jìn)行驗(yàn)證。
松散schema允許使用復(fù)雜的內(nèi)部結(jié)構(gòu)(嵌套實(shí)體)構(gòu)造實(shí)體的類,也允許改變特定實(shí)體的結(jié)構(gòu)。這個(gè)更能帶來(lái)了兩個(gè)重要的便利:
通過(guò)嵌套的實(shí)體,最小化了一對(duì)多的關(guān)系,也因此減少了連接(join)。
異構(gòu)業(yè)務(wù)實(shí)體的模型可以使用一個(gè)文檔集合或者一個(gè)數(shù)據(jù)表。松散schema掩藏了這種建模和業(yè)務(wù)實(shí)體之間“技術(shù)”上的差異。
我們用下面的圖來(lái)說(shuō)明這些便利。該圖描繪了對(duì)電子商務(wù)領(lǐng)域中一個(gè)產(chǎn)品實(shí)體進(jìn)行的建模。首先我們可以認(rèn)為所有的產(chǎn)品都有一個(gè)ID、價(jià)格(Price)和描述(Description)。進(jìn)一步來(lái)看,我們發(fā)現(xiàn)不同類型的產(chǎn)品有不同的屬性,如圖書包含作者信息,而牛仔褲有長(zhǎng)度屬性。這些屬性中間的某些屬性天生就有一對(duì)多或這多對(duì)多的特性,比如音樂唱片中的曲目。
更進(jìn)一步來(lái)看,可能有些實(shí)體不可能使用固定的類型進(jìn)行建模。例如,不同品牌的牛仔褲的屬性是不固定的,而每個(gè)制造商出產(chǎn)的牛仔褲的屬性也是不一致的。在規(guī)范化的關(guān)系型數(shù)據(jù)模型中雖然這些問題都可以解決,但方法很猥瑣。松散schema軟架構(gòu)允許只使用一個(gè)聚合(Aggregation)(產(chǎn)品)就能對(duì)所有類型的產(chǎn)品及其屬性進(jìn)行建模:
內(nèi)嵌的去規(guī)范化會(huì)在性能和一致性上對(duì)更新操作造成很大的影響,所以要特別注意更新過(guò)程。
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù), BigTable的風(fēng)格數(shù)據(jù)庫(kù)
3、 應(yīng)用端連接(Application Side Joins)
很少有NoSQL解決方案支持連接。NoSQL“問題導(dǎo)向”性質(zhì)的后果就是,通常在設(shè)計(jì)時(shí)處理join,而關(guān)系型模型是在執(zhí)行查詢時(shí)處理join。查詢時(shí)處理join幾乎肯定會(huì)帶來(lái)性能上的損失,但在許多情況下,可使用去規(guī)范化和聚合,即嵌入嵌套實(shí)體來(lái)避免join。當(dāng)然,join在許多情況下是不可避免的,而且應(yīng)該由應(yīng)用程序處理。主要的用例:
多對(duì)多關(guān)系往往是通過(guò)鏈接(link)建模的,這需要join。
聚合操作往往不適合內(nèi)部實(shí)體會(huì)被頻繁修改的場(chǎng)景。通常更好的辦法是將發(fā)生的事情作為一條新的記錄保留,并在查詢的時(shí)候?qū)⑺杏涗涀鰆oin,而不是去更改值。例如,對(duì)于一個(gè)信息系統(tǒng)而言,可以用嵌套包含了Message實(shí)體的User實(shí)體來(lái)建模。但是,如果會(huì)經(jīng)常地添加消息,更好的辦法可能是把Message提取出來(lái)作為獨(dú)立實(shí)體,并在查詢時(shí)再將其與User進(jìn)行連接:
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格數(shù)據(jù)庫(kù),圖形數(shù)據(jù)庫(kù)
5.一般建模技術(shù)
在本節(jié)中,我們將討論適用于各種NoSQL實(shí)現(xiàn)的一般建模技術(shù)。
1、 原子聚合(Atomic Aggregates)
許多NoSQL解決方案提供了有限的事務(wù)支持,雖然有些NoSQL不支持。在某些情況下,人們還可以使用分布式鎖或應(yīng)用程序管理的MVCC機(jī)制實(shí)現(xiàn)事務(wù)行為,但常見的是使用聚合技術(shù)來(lái)對(duì)數(shù)據(jù)建模,以保證一些ACID特性。
強(qiáng)大的事務(wù)處理機(jī)制對(duì)于關(guān)系型數(shù)據(jù)庫(kù)而言是不可或缺的,其中原因之一就是規(guī)范化的數(shù)據(jù)通常需要在多個(gè)地方進(jìn)行更新。另一方面,聚合允許一個(gè)單個(gè)業(yè)務(wù)實(shí)體存儲(chǔ)為一個(gè)文件,行或鍵值對(duì),從而可以對(duì)其進(jìn)行原子性的更新:
當(dāng)然,做為一種數(shù)據(jù)建模技術(shù),原子聚合并不是一個(gè)完善的事務(wù)型解決方案,但如果存儲(chǔ)能提供原子性、鎖或者TAS(test-and-set,測(cè)試并設(shè)置)指令上的一些擔(dān)保,那原子聚合就是可行的。
(譯者注:即將需要事務(wù)性操作的業(yè)務(wù)數(shù)據(jù)聚合放在一起,存儲(chǔ)在一個(gè)NoQSQL提供或者應(yīng)用能提供原子性操作的數(shù)據(jù)結(jié)構(gòu)中。使用HBase時(shí),將某個(gè)用戶某個(gè)業(yè)務(wù)的所有數(shù)據(jù),如上圖,用一行存儲(chǔ)就是這種模式的應(yīng)用。)
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格數(shù)據(jù)庫(kù)
2、 可枚舉主鍵(Enumerable Keys)
也許無(wú)序鍵-值數(shù)據(jù)模型最大的好處就是可以通過(guò)將主鍵哈希的辦法把實(shí)體數(shù)據(jù)分別存儲(chǔ)在多個(gè)服務(wù)器上。排序使事情變得更加復(fù)雜,但是即使存儲(chǔ)不提供這樣的功能,有時(shí)應(yīng)用程序也能利用到有序主鍵的優(yōu)勢(shì)。讓我們將對(duì)電子郵件建模作為一個(gè)例子:
某些NoSQL存儲(chǔ)提供原子計(jì)數(shù)器,能生成一個(gè)順序化的ID。在這種情況下,可以使用userID_messageID作為一個(gè)復(fù)合鍵來(lái)存儲(chǔ)消息。如果最新的消息ID是已知的,那就可以遍歷以前的消息。另外,對(duì)于任何一個(gè)給定的消息ID,也可以向前或向后進(jìn)行遍歷。
也可以將消息分桶(bucket),例如,每天的數(shù)據(jù)放到一個(gè)桶里。這樣就允許從任何指定日期或當(dāng)前日期開始,向前或向后遍歷一個(gè)郵箱。
適用性:鍵值存儲(chǔ)
(譯者注:能利用主鍵的一些自然或業(yè)務(wù)維度的特征,將隨機(jī)讀寫轉(zhuǎn)換為順序讀寫能提高遍歷性能,同時(shí)能方便應(yīng)用邏輯編寫。但需要注意對(duì)分布式部署時(shí)并發(fā)寫的影響以及對(duì)于業(yè)務(wù)的過(guò)度耦合。對(duì)于無(wú)序主鍵和有序主鍵的討論可以參見《HBase權(quán)威指南》中Schema設(shè)計(jì)章節(jié)。)
3、 降維(Dimensionality Reduction)
降維這種技術(shù)允許將一個(gè)多維數(shù)據(jù)模型映射到一個(gè)鍵-值模型或其他非多維模型。
傳統(tǒng)的地理信息系統(tǒng)使用四叉樹(Quadtree)或R樹(R-tree)的某種變形來(lái)做索引。這些結(jié)構(gòu)需要就地完成更新操作,因此在數(shù)據(jù)量很大時(shí),維護(hù)開銷相當(dāng)?shù)拇?。另一種方法是對(duì)這個(gè)二維結(jié)構(gòu)進(jìn)行遍歷,并將其扁平化為一個(gè)普通的條目列表。使用這種技術(shù)的一個(gè)眾所周知的例子是Geohash。 Geohash使用類似Z形狀的路線來(lái)掃描整個(gè)二維空間,每次移動(dòng)根據(jù)行進(jìn)方向被編碼為0或1。交錯(cuò)位的經(jīng)度和緯度上的變更移動(dòng)以及移動(dòng)。編碼過(guò)程在下圖中進(jìn)行了說(shuō)明,其中黑色和紅色位分別代表經(jīng)度和緯度:
如圖所示,Geohash的一個(gè)重要特性是能夠通過(guò)這種逐位編碼的近似程度來(lái)估計(jì)區(qū)域之間的距離。Geohash編碼允許使用簡(jiǎn)單普通的數(shù)據(jù)模型來(lái)存儲(chǔ)地理信息,比如用有序鍵值保存空間上的聯(lián)系。[6.1]講述了BigTable中的降維技術(shù)。更多有關(guān)Geohash及其相關(guān)技術(shù)的信息可以在[6.2]和[6.3]中找到。
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù), BigTable風(fēng)格的數(shù)據(jù)庫(kù)
(譯者注:通過(guò)交織編碼方式來(lái)能將原本需要多維度標(biāo)示的數(shù)據(jù),如cube,存儲(chǔ)到一維的鍵值存儲(chǔ)系統(tǒng)中,這是一種非常重要的建模模式:提供了不同縮放等級(jí)下在多維空間中鄰接的數(shù)據(jù)仍然順序存儲(chǔ),遍歷高效;同時(shí)不同主鍵從前向后的相似度和空間距離的遠(yuǎn)近相一致,能通過(guò)鍵值的簡(jiǎn)單順序比較判斷其位置“相似度”。
它的應(yīng)用遠(yuǎn)遠(yuǎn)不只地理信息的表示,有多個(gè)維度屬性不同粒度的數(shù)據(jù)表示都能用到這個(gè)技術(shù),比如線下銷售交易數(shù)據(jù)通常都有時(shí)間和分支結(jié)構(gòu)信息,用戶查詢銷售數(shù)據(jù)時(shí)通常先查詢一個(gè)大的區(qū)域,而且使用的時(shí)間段跨度也比較大,需要查詢更具體的信息時(shí)同時(shí)縮小地區(qū)和時(shí)間來(lái)逐步定位細(xì)節(jié),這時(shí)分支機(jī)構(gòu)和時(shí)間就好比是經(jīng)度維度的兩個(gè)軸,通過(guò)交織時(shí)間和分支結(jié)構(gòu)編碼的方式建立主鍵,能很好的滿足這種場(chǎng)景。而單純的使用時(shí)間或分支機(jī)構(gòu)做主鍵或索引卻都不能適應(yīng)上述場(chǎng)景。)
4、 索引表(Index Table)
索引表是一個(gè)非常簡(jiǎn)單的技術(shù),它在內(nèi)部不支持索引的存儲(chǔ)上提供索引的支持。這類存儲(chǔ)中最重要的一類就是BigTable風(fēng)格的數(shù)據(jù)庫(kù)。索引表的想法是按照訪問模式所需要的鍵來(lái)創(chuàng)建和維護(hù)一個(gè)特殊的表。例如,有一個(gè)主表,存儲(chǔ)了可以通過(guò)用戶ID直接訪問的用戶帳戶。查詢指定城市的所有用戶可以通過(guò)一個(gè)額外的用城市做主鍵的表來(lái)支持:
索引表可以在每一個(gè)主表記錄更新時(shí)更新或者使用批模式更新。無(wú)論哪種方式,它會(huì)導(dǎo)致額外的性能損失,并帶來(lái)數(shù)據(jù)一致性上的問題。
可以認(rèn)為索引表是一種對(duì)關(guān)系數(shù)據(jù)庫(kù)實(shí)例化視圖的模擬。
適用性: BigTable風(fēng)格的數(shù)據(jù)庫(kù)
5、 組合主鍵索引(Composite Key Index)
復(fù)合主鍵是一個(gè)非常通用的技術(shù),但尤其在主鍵有序存儲(chǔ)時(shí)極其有用。復(fù)合主鍵結(jié)合二次排序就能建立起一種多維索引,這和前面所述的降維技術(shù)在原理上是類似的。例如,假設(shè)我們有一組記錄,每個(gè)記錄是一個(gè)用戶統(tǒng)計(jì)數(shù)據(jù)。如果我們要按用戶來(lái)自的地區(qū)來(lái)聚合這些統(tǒng)計(jì)資料,我們可以使用這樣的主鍵格式(State:City:UserId)。如果主鍵的存儲(chǔ)支持通過(guò)部分匹配來(lái)選取范圍(如BigTable風(fēng)格的數(shù)據(jù)庫(kù)),那就可以在特定的州(State)或者城市(City)的記錄上做遍歷:
適用性: BigTable風(fēng)格的數(shù)據(jù)庫(kù)
(譯者注:在使用HBase這類BigTable風(fēng)格的數(shù)據(jù)庫(kù)時(shí),如果主鍵只使用一個(gè)字段/域的信息簡(jiǎn)直是暴殄天物。使用多字段組合主鍵不僅能解決主鍵值不能重復(fù)的問題,還能提高對(duì)于數(shù)據(jù)子集二次查找時(shí)的性能。)
6、 組合主鍵的聚合
復(fù)合主鍵不僅可用于作索引,還可以為不同類型分組。讓我們來(lái)看一個(gè)例子。有一個(gè)巨大的日志數(shù)組,記錄了互聯(lián)網(wǎng)用戶和他們?cè)L問不同的網(wǎng)站(點(diǎn)擊流)的信息。我們的目標(biāo)是對(duì)于每個(gè)唯一用戶計(jì)算出每個(gè)站點(diǎn)的點(diǎn)擊數(shù)量。這類似于下面的SQL查詢:
我們可以使用將用戶名當(dāng)前綴的組合主鍵來(lái)對(duì)這種情況進(jìn)行建模:
我們的想法是將一個(gè)用戶的所有記錄放置在一起,這樣就可能將其全部加載到內(nèi)存中(一個(gè)用戶不會(huì)產(chǎn)生太多的事件),并使用哈希表或其他方法消除掉重復(fù)的網(wǎng)站。另一種技術(shù)是將用戶做為主鍵,每次事件到達(dá)時(shí)將網(wǎng)站添加到這條數(shù)據(jù)的后部。然而,在大多數(shù)實(shí)現(xiàn)中,修改數(shù)據(jù)一般比插入數(shù)據(jù)的效率低。
適用性:有序鍵值存儲(chǔ),BigTable風(fēng)格的數(shù)據(jù)庫(kù)
7、 倒排搜索-直接聚合
這種技術(shù)更像是數(shù)據(jù)處理模式,而不是數(shù)據(jù)建模。然而,數(shù)據(jù)模型也受這種模式使用的影響。這種技術(shù)的主要思想是使用索引來(lái)找到滿足條件的數(shù)據(jù),但聚合操作還是使用原來(lái)的方式或者全表掃描。讓我們來(lái)考慮一個(gè)例子。有一堆的日志數(shù)據(jù)記錄了互聯(lián)網(wǎng)用戶和他們?cè)L問不同的網(wǎng)站(點(diǎn)擊流,click stream)的信息。假設(shè)每條記錄都包括用戶ID、用戶所屬類別(男性、女性、博主(Blogger)等)、用戶來(lái)自的城市以及訪問的網(wǎng)址。我們的目標(biāo)是找出滿足條件(網(wǎng)址、城市等)的觀眾,并將這堆觀眾(如符合標(biāo)準(zhǔn)的用戶集合)中出現(xiàn)的不同用戶按類別歸類。
很明顯,滿足條件的用戶可以通過(guò)像{類別->[用戶ID]}或{網(wǎng)站->[用戶ID]}這樣的倒排索引表非常高效地查找到。使用這樣的倒排索引,可以得到所要的用戶ID的交集或者并集(如果用戶ID被存儲(chǔ)為排有序的列表或位圖,這就可以非常高效地實(shí)現(xiàn)),從而獲得目標(biāo)用戶。但如果目標(biāo)用戶是使用類似這樣的聚集查詢描述的:
那如果類別的數(shù)量很大,就不能用倒排索引做有效的處理。要解決這個(gè)問題,可以用{用戶名->[分類集合]}的形式創(chuàng)建直接索引(Direct Index),然后遍歷它來(lái)建立最終報(bào)表。此架構(gòu)示意如下圖:
最后需要提示的是,我們需要知道如果隨機(jī)地訪問目標(biāo)用戶中每一個(gè)用戶ID所對(duì)應(yīng)記錄,這樣做的效率可能很低??梢酝ㄟ^(guò)利用批量查詢處理解決這個(gè)問題。這意味著,一些數(shù)量的用戶集可以被預(yù)先計(jì)算(針對(duì)不同的報(bào)表?xiàng)l件),然后可以通過(guò)對(duì)直接索引表或倒排索引表進(jìn)行一次全表掃描從而計(jì)算出這批目標(biāo)用戶的所有報(bào)告。
適用性:鍵值存儲(chǔ),BigTable風(fēng)格的數(shù)據(jù)庫(kù),文檔數(shù)據(jù)庫(kù)
分層建模技術(shù)(Hierarchy Modeling Techniques)
(譯者注:如果需要通過(guò)屬性或者類別來(lái)快速查找對(duì)應(yīng)的實(shí)體,這樣的索引比不可少?,F(xiàn)在很火的用大數(shù)據(jù)分析用戶畫像不就需要像{用戶標(biāo)簽->用戶群}這樣的結(jié)構(gòu)么。)
8、 樹聚合(Tree Aggregation)
可以將一條單獨(dú)的記錄或者文件的模型建成樹,甚至是任意的圖(通過(guò)去規(guī)范化)。
在樹會(huì)被一次性訪問的場(chǎng)景中(例如,博客的整個(gè)評(píng)論樹會(huì)被讀取,并顯示在一篇文章的頁(yè)面中),這個(gè)技術(shù)很高效。
搜索和訪問任意條目可能有問題。
在大多數(shù)NoSQL的實(shí)現(xiàn)中,更新操作的效率低下(同相互獨(dú)立的節(jié)點(diǎn)相比)。
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù)
(譯者注:這是文檔型NoSQL的天下,對(duì)于無(wú)需跨樹join的場(chǎng)景,不僅讀寫效率高,而且能很好的支持局部性的事務(wù)應(yīng)用。)
9、 鄰接列表(Adjacency Lists)
鄰接列表是一個(gè)簡(jiǎn)單的圖型建模方法——每個(gè)節(jié)點(diǎn)作為一個(gè)單獨(dú)記錄建模,其中有包含直接祖先的數(shù)組或包含后代的數(shù)組。它允許通過(guò)其父母或子女的標(biāo)識(shí)符來(lái)搜索一個(gè)節(jié)點(diǎn),當(dāng)然也可以通過(guò)查詢一次前進(jìn)一步的方式來(lái)遍歷一個(gè)圖。無(wú)論對(duì)于深度優(yōu)先或廣度優(yōu)先遍歷而言,要在整個(gè)子樹中找到一個(gè)給定的節(jié)點(diǎn),這種方法的效率通常不高。
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù)
10、 物化路徑
物化路徑是一種有助于避免在樹型結(jié)構(gòu)上做遞歸遍歷的技術(shù)。也可以認(rèn)為這是一種去規(guī)范化的技術(shù)。其設(shè)計(jì)思想是用一個(gè)節(jié)點(diǎn)所有的父節(jié)點(diǎn)或者子女節(jié)點(diǎn)來(lái)標(biāo)識(shí)該節(jié)點(diǎn),這樣就有可以不用遍歷而得到一個(gè)節(jié)點(diǎn)的所有祖先節(jié)點(diǎn)或者衍生節(jié)點(diǎn):
因?yàn)檫@個(gè)技術(shù)可以將層次結(jié)構(gòu)轉(zhuǎn)換成扁平化的文檔,所以它對(duì)于全文搜索引擎特別地有用。從上圖中可以看出,在男鞋(Men’s Shoes)類別下所有的產(chǎn)品或者子類別可以簡(jiǎn)單的通過(guò)查詢一個(gè)類別名稱而得到。這個(gè)查詢很短。
物化路徑的存儲(chǔ)方式可以是一個(gè)ID的集合,或者一個(gè)是包含級(jí)聯(lián)ID的字符串。后一種方式允許使用正則表達(dá)式,來(lái)查找那些指定部分的路徑符合某種條件的節(jié)點(diǎn)。此種方法如在下圖(路徑也包括了節(jié)點(diǎn)自身)所示:
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù),搜索引擎
11、 嵌套集合(Nested Sets)
在對(duì)類似樹型結(jié)構(gòu)進(jìn)行建模時(shí),嵌套集合是個(gè)標(biāo)準(zhǔn)的做法。它在關(guān)系數(shù)據(jù)庫(kù)中廣泛被使用,然而它也完全適用于鍵值存儲(chǔ)和文檔數(shù)據(jù)庫(kù)。其設(shè)計(jì)思想是在用數(shù)組來(lái)存儲(chǔ)樹的葉子節(jié)點(diǎn)(譯者:每個(gè)葉子節(jié)點(diǎn)對(duì)應(yīng)數(shù)組中的一個(gè)位置下標(biāo)),并將每個(gè)非葉結(jié)點(diǎn)映射為一個(gè)葉子節(jié)點(diǎn)的范圍,這個(gè)范圍就是開始葉子節(jié)點(diǎn)和結(jié)束葉子節(jié)點(diǎn)在數(shù)組中的位置下標(biāo)。如下圖所示:
這個(gè)結(jié)構(gòu)對(duì)于不變數(shù)據(jù)來(lái)講非常有效,因?yàn)樗加玫膬?nèi)存小,并且可以在不遍歷樹的情況下得到一個(gè)給定節(jié)點(diǎn)的所有葉子節(jié)點(diǎn)。然而,因?yàn)樵黾右粋€(gè)葉子節(jié)點(diǎn)會(huì)帶來(lái)位置下標(biāo)的大量更新,所以插入和更新的操作代價(jià)是相當(dāng)?shù)母摺?/p>
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù)
(譯者注:適用于字典表、按日期排序的歷史日志數(shù)據(jù)表等。)
12、 扁平化嵌套文件:字段名稱編號(hào)
搜索引擎通常工作在扁平化的文檔之上,即每個(gè)文檔由兩個(gè)扁平列表組成,這兩個(gè)列表分別記錄了字段的名稱和它對(duì)應(yīng)的值。數(shù)據(jù)建模的目標(biāo)是將業(yè)務(wù)實(shí)體映射為簡(jiǎn)單無(wú)結(jié)構(gòu)的文檔,但如果實(shí)體內(nèi)部的結(jié)構(gòu)很復(fù)雜,這就難辦了。一個(gè)典型的困難就是層次結(jié)構(gòu)模型,比如要將內(nèi)部嵌套了文檔的文檔映射為簡(jiǎn)單無(wú)結(jié)構(gòu)的文檔。讓我們來(lái)考慮下面的例子:
每一個(gè)業(yè)務(wù)實(shí)體是一份某種形式的簡(jiǎn)歷,其中包含了這個(gè)人的名字,和枚舉了他或她所具有的技能以及相應(yīng)技能水平的列表。為這樣的實(shí)體建模的一個(gè)很顯然的方式就是是建立的簡(jiǎn)單無(wú)結(jié)構(gòu)文檔,里面包含Skill和Level字段的列表。這種模式允許通過(guò)技能或水平來(lái)搜索某個(gè)人,但將這兩個(gè)字段聯(lián)合起來(lái)搜索卻容易導(dǎo)致虛假匹配,正如上圖所述。(譯者:簡(jiǎn)單的AND操作不能感知技能以及其水平的對(duì)應(yīng)關(guān)系。)
在[4.6]中提出了一種解決這個(gè)問題的方法。這項(xiàng)技術(shù)的主要思想是將每一項(xiàng)技能以及相應(yīng)的水平聯(lián)合起來(lái)組成一個(gè)配對(duì)(pair),并使用下標(biāo)標(biāo)識(shí)成為Skill_i和Level_i。在搜索時(shí)需要同時(shí)查詢所有這些對(duì)值(查詢中OR條件語(yǔ)句的個(gè)數(shù)是和一個(gè)人所能具有的技能的最大值相同):
這種方法實(shí)際上沒有可擴(kuò)展性,因?yàn)殡S著嵌套結(jié)構(gòu)的數(shù)目的增長(zhǎng)會(huì)迅速增加查詢的復(fù)雜度。
適用性:搜索引擎
(譯者注:如果你對(duì)于使用索引來(lái)加速查詢無(wú)計(jì)可施,尤其對(duì)用戶將來(lái)如何查詢數(shù)據(jù)一無(wú)所知,無(wú)法在設(shè)計(jì)模型時(shí)進(jìn)行優(yōu)化時(shí),這就是最后的稻草:使用全文搜索工具。它至少能在可接受的時(shí)間內(nèi)能查出點(diǎn)東西來(lái)^o^。)
13、 扁平化嵌套文件:近似查詢
在[4.6]中還描述了另一種可以解決嵌套文件問題的技術(shù)。它的想法是使用近似的查詢,將文檔中單詞之間的距離限制在可以接受的距離范圍以內(nèi)。在下面的圖中,所有的技能和水平都被索引到了一個(gè)叫SkillAndLevel的域。使用“Excellent”和“Poetry”進(jìn)行查詢表示的話,就會(huì)查找到這兩個(gè)單詞鄰接的條目:
[4.3]講述了在Solr之上使用這種技術(shù)的一個(gè)成功案例。
適用性:搜索引擎
(譯者注:同上,但查詢召回率高,會(huì)出現(xiàn)假匹配,有臟數(shù)據(jù)。)
14、 批量圖處理
在瀏覽一個(gè)指定節(jié)點(diǎn)的相鄰節(jié)點(diǎn)或?yàn)g覽兩個(gè)或幾個(gè)節(jié)點(diǎn)之間的關(guān)系時(shí),像Neo4j這樣的圖形數(shù)據(jù)庫(kù)的性能出奇的好。然而,通用圖形數(shù)據(jù)庫(kù)對(duì)大圖做全局性處理不是很高效,因?yàn)閿U(kuò)展性不好。分布式圖形處理可以使用MapReduce或者消息傳遞(Message Passing)模式來(lái)實(shí)現(xiàn)。我以前的一篇文章就介紹了一種這樣的模式。這種方法使用了鍵值存儲(chǔ)、文檔數(shù)據(jù)庫(kù)和BigTable風(fēng)格的數(shù)據(jù)庫(kù),能處理大型的圖形。
適用性:鍵值存儲(chǔ),文檔數(shù)據(jù)庫(kù),BigTable風(fēng)格的數(shù)據(jù)庫(kù)
作者介紹:陳飚,Cloudera售前技術(shù)經(jīng)理、行業(yè)領(lǐng)域顧問、資深方案架構(gòu)師,原Intel Hadoop發(fā)行版核心開發(fā)人員。2006年加入Intel編譯器部門從事服務(wù)器中間件軟件開發(fā),擅長(zhǎng)服務(wù)器軟件調(diào)試與優(yōu)化,曾帶領(lǐng)團(tuán)隊(duì)開發(fā)出世界上性能領(lǐng)先的 XSLT 語(yǔ)言處理器。2010 年后開始Hadoop 產(chǎn)品開發(fā)及方案顧問,先后負(fù)責(zé)Hadoop 產(chǎn)品化、HBase 性能調(diào)優(yōu),以及行業(yè)解決方案顧問,已在交通、通信等行業(yè)成功實(shí)施并支持多個(gè)上百節(jié)點(diǎn)Hadoop 集群。