MySQL到NoSQL:數(shù)據(jù)的重思和查詢方式的轉(zhuǎn)換
從關(guān)系型數(shù)據(jù)庫(kù)轉(zhuǎn)移至NoSQL數(shù)據(jù)庫(kù)——比如從MySQL轉(zhuǎn)移到Couchbase,你需要對(duì)你的數(shù)據(jù)進(jìn)行再思考。至于為什么是Couchbase而不是MongoDB什么的,因?yàn)椴┪牡淖髡進(jìn)C Brown是現(xiàn)任Couchbase副總裁,所以你懂得;同時(shí)這篇Couchbase博文還涉及到遷移后對(duì)查詢的影響。
以下為譯文:
如果你有一個(gè)建立在MySQL上的數(shù)據(jù)庫(kù),你可能就會(huì)考慮是否需要以及更重要的如何將數(shù)據(jù)庫(kù)(和你的應(yīng)用程序)轉(zhuǎn)移到Couchbase上。***的絆腳石不在Couchbase建立或者是存儲(chǔ)信息方面(盡管他們也很重要),而是數(shù)據(jù)的重思,你需要使用另一種方式去處理你的數(shù)據(jù),然后對(duì)應(yīng)用程序作出相應(yīng)的變化。
下面將著眼如何把MySQL數(shù)據(jù)庫(kù)結(jié)構(gòu)轉(zhuǎn)換成Couchbase Server,并針對(duì)兩個(gè)數(shù)據(jù)庫(kù)的查詢方式改變進(jìn)行討論。
首先:數(shù)據(jù)結(jié)構(gòu)的重思
MySQL(以及其它的SQL類型并且以表格為基礎(chǔ)的數(shù)據(jù)庫(kù))強(qiáng)迫你將數(shù)據(jù)打造成表格的形式。你所有的數(shù)據(jù)就是一張表,當(dāng)你儲(chǔ)存復(fù)雜結(jié)構(gòu)類型數(shù)據(jù)時(shí),一個(gè)單獨(dú)的數(shù)據(jù)片可能拆分成多于一張的表格。對(duì)于一些應(yīng)用程序以及數(shù)據(jù)類型,如此存儲(chǔ)數(shù)據(jù)是一個(gè)***的邏輯以及合理的途徑。而同樣對(duì)于某些應(yīng)用程序,使用這樣的方法去存儲(chǔ)數(shù)據(jù)并不是很適合。
下面看一個(gè)典型的例子,一個(gè)recipe(食譜)數(shù)據(jù)庫(kù)。因?yàn)镃heffy.com是建立在MySQL之上,所以MC Brown對(duì)此非常清楚。基礎(chǔ)的表格結(jié)構(gòu)是一個(gè)核心表,稱為recipe,包括了食譜的name、subtitle、description和servings。當(dāng)然還有一些其它的recipe信息,比如:成分清單(Ingredients)、方法步驟(Method)、元數(shù)據(jù)(Metadata)以及通過(guò)一個(gè)獨(dú)立的recipe ID連接到原recipe表的關(guān)鍵詞(Keywords)。你可以在下圖中看到這些主要部分:

這個(gè)結(jié)構(gòu)有一些潛在的好處,一些特定的操作可以非常簡(jiǎn)單的完成。舉個(gè)例子,比如說(shuō)查詢一些包含原料“carrot(胡蘿卜)”的recipe(食譜)。你可以從Ingredients表中查找“carrot”,并鑒于這點(diǎn)得到一個(gè)匹配的recipe列表。通過(guò)使用join你可以獲得一個(gè)recipe列表,從中獲取他們的title以及一些其它的信息,通過(guò)搜索Ingredient表,使用join連接兩張表中的recipe ID。

當(dāng)然這種查詢方法很簡(jiǎn)單,可以收集到一個(gè)食譜的所有信息。然而當(dāng)你想給用戶演示這個(gè)食譜時(shí),將會(huì)變得很復(fù)雜。你可以通過(guò)一個(gè)單獨(dú)的查詢來(lái)完成,然而有時(shí)候通過(guò)幾個(gè)查詢來(lái)完成這個(gè)更容易,分別獲取recipe、ingredients、metadata等表格的數(shù)據(jù)。在應(yīng)用程序?qū)樱ㄟ^(guò)建立對(duì)象就可以自動(dòng)的完成這些操作,同時(shí)這也是此類操作的基礎(chǔ)方法。
對(duì)于許多用戶和應(yīng)用程序,通常會(huì)建立一個(gè)特殊的層去做這些事情,或者是選用一個(gè)對(duì)象映射系統(tǒng)將底層表格式的數(shù)據(jù)映射成應(yīng)用程序使用的高等級(jí)對(duì)象。這里的recipe就是一個(gè)例子,在其它各種各樣的應(yīng)用程序中也有類似的存在,包括invoicing (invoice、supplier、 destination, invoice lines) 和 blog posts (post content、keywords、creator、 comments)。
這種基于表格的解決方案本身并沒(méi)有什么問(wèn)題,但是在這種場(chǎng)景下,key被跨表格的儲(chǔ)存,同樣這也意味著需要保持這幾個(gè)表格的同步。舉個(gè)例子,當(dāng)某條記錄被刪除時(shí)會(huì)出現(xiàn)什么樣的情況?你必須刪掉連接到原表上的其它記錄(不管是手動(dòng)刪除或者是級(jí)聯(lián)刪除)。類似的,當(dāng)加載一個(gè)食譜信息時(shí),你可能需要運(yùn)行5-10個(gè)查詢。
Couchbase使用了一個(gè)不同的方法。取代將信息分割存入多張表格,在Couchbase中你只需要儲(chǔ)存一個(gè)單獨(dú)的結(jié)構(gòu)(JavaScript Object Notation JSON)格式。JSON格式允許很多復(fù)雜的數(shù)據(jù)結(jié)構(gòu),包括字段、對(duì)象和標(biāo)量類型,可以用它們組成一個(gè)完整的記錄。這就意味著你可以使用一個(gè)“文檔(document)”代替之前你使用多個(gè)表格來(lái)儲(chǔ)存實(shí)體(recipe,blog post)。

現(xiàn)在只需要在一處就可以對(duì)整個(gè)recipe進(jìn)行操作,也就是只需要一個(gè)操作就可以從Couchbase數(shù)據(jù)庫(kù)中獲得你想要的信息。
這里對(duì)內(nèi)容沒(méi)有強(qiáng)制的結(jié)構(gòu)或者是定義,Couchbase數(shù)據(jù)庫(kù)中的任何文檔都可以儲(chǔ)存任何的信息和結(jié)構(gòu)。然而你還可以使用一個(gè)驗(yàn)證程序來(lái)檢查提供給數(shù)據(jù)庫(kù)文檔的結(jié)構(gòu),驗(yàn)證可以同時(shí)針對(duì)字段以及字段的類容。
鑒于這里沒(méi)有嚴(yán)格的數(shù)據(jù)結(jié)構(gòu)類型,它可以給存儲(chǔ)帶來(lái)更高的靈活性。舉個(gè)例子,你不需要額外的操作就可以向recipe文檔中添加新的部分“食譜的提供人”。
這里同樣沒(méi)有多重表這個(gè)概念,這里只有數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)中只包含了文檔。如果你想讓同一個(gè)數(shù)據(jù)庫(kù)支持各種不同類型的信息,你可以向文檔中加入字段。比如你可以如此來(lái)定義一個(gè)recipe:

那么type就可以作為標(biāo)記在數(shù)據(jù)庫(kù)其它的地方使用,用以識(shí)別(選擇)你正在加載的數(shù)據(jù)。 #p#
其次:如果什么都是文檔,那么你該如何查詢
***節(jié)中已經(jīng)討論過(guò)如何在MySQL上使用一條簡(jiǎn)單的SQL語(yǔ)句來(lái)獲得一個(gè)原料中包含胡蘿卜菜譜的列表。在MySQL中我們通過(guò)搜索ingredients表中與carrot匹配的recipe ID值,然后通過(guò)join從recipe表中獲得recipe title。出于速度考慮,你可能會(huì)使用一個(gè)索引來(lái)提升查詢的響應(yīng)時(shí)間,用以保存單獨(dú)的每一條記錄。
在Couchbase所有數(shù)據(jù)都是文檔,并且這里也沒(méi)有嵌入的方法用以查詢表格中的字段,這里既沒(méi)有字段也沒(méi)有表格。因?yàn)镃ouchbase中本就沒(méi)有嚴(yán)格的數(shù)據(jù)結(jié)構(gòu)模型(同樣也沒(méi)有方法讓數(shù)據(jù)庫(kù)引擎去確定自由格式文檔中的字段),那么我們?cè)撊绾瓮瓿稍谄胀ū砀裆系牟僮髂?
Couchbase支持一個(gè)構(gòu)造稱為view,而這個(gè)view與MySQL中的view非常神似;除下在Couchbase中,view是唯一從數(shù)據(jù)庫(kù)文檔中獲得列表的方法,而在MySQL中view只是豐富了一個(gè)選擇。View事實(shí)上定義了3件事情:
View中包含的信息結(jié)構(gòu)。你可以想象成表結(jié)構(gòu)的定義,就像你在MySQL中建立一張表。
用以搜索的字段或者是信息。一個(gè)view將輸出兩項(xiàng)結(jié)果,key和value。Key將被傳入方法以發(fā)現(xiàn)你需要搜索什么,更具體的說(shuō)就是你需要查詢的數(shù)據(jù)庫(kù)內(nèi)容。
結(jié)構(gòu)和key上的索引。索引同樣用于提升搜索的性能。
View使用JavaScript在一個(gè)設(shè)計(jì)文檔中進(jìn)行定義,使用一個(gè)文檔作為參數(shù)的函數(shù)。一旦view被構(gòu)造,數(shù)據(jù)庫(kù)中的每個(gè)文檔都會(huì)提供給view,然后view查詢并輸出你需要的結(jié)果。不用擔(dān)心JavaScript,JavaScript將只在服務(wù)器上執(zhí)行(不會(huì)在客戶端上執(zhí)行)。
現(xiàn)在回到MySQL,一個(gè)沒(méi)有where的查詢,查詢輸出選擇的字段,并且在輸出結(jié)果中構(gòu)造一個(gè)匹配行的列表。下面,詳解一個(gè)SQL語(yǔ)句:

當(dāng)在MySQL上運(yùn)行一個(gè)查詢,MySQL服務(wù)器從一張或者多張表中取得信息,然后構(gòu)建輸出結(jié)果列表:

在Couchbase中,View從單一的文檔中獲取記錄,并且在處理過(guò)程中附帶建立一個(gè)索引。結(jié)果則是view抓取的所有文檔列表。

在MySQL中執(zhí)行一個(gè)帶where的查詢,索引一般被期望幫助你查詢需要的記錄:

而在Couchbase中,生成的view就是你的表格,當(dāng)你對(duì)view進(jìn)行查詢,Couchbase使用這些鍵的值(以及生成的相關(guān)索引)和過(guò)濾后(你指定)的返回信息,生成最終的匹配列表。

總結(jié)
上文主要針對(duì)MySQL到Couchbase的轉(zhuǎn)變,通過(guò)上面的兩個(gè)步驟基本上可以完成這個(gè)過(guò)渡。然而一些更高級(jí)的操作必然需要一些更多的思考,MC Brown在Couchbase博客同樣發(fā)布了轉(zhuǎn)換的第二部分,包括了更多高級(jí)查詢的思考。