2014年4月9日 星期三

多語言維持 Polyglot Persistence

    Martin Fowler和Pramod Sadalage兩位是近來頗為暢銷的「NoSQL Distilled」一書的作者(國內有翻譯本,名稱為「搞懂NoSQL的15堂課」),在該書中,除了介紹NoSQL的概念及方法之外,在第十三章中還介紹了一個他們所推廣的理念:Polyglot Persistence

    Polyglot Persistence可翻譯為多語言維持,若單純從字面上很難馬上體會出它的意義,但其實更早之前,在2006年Neal Ford曾經創造了類似的「Polyglot Programming」一詞,他希望闡述一個概念,就是每種不同的程式語言皆有其最適合解決的問題類型,針對不同工作需求選擇對的程式語言,會比僅嚐試僅用單一程式語言來解決所有面臨的問題更具有生產力,因此針對此觀點,他提出了「Polyglot Programming」,建議應用程式應混合不同的程式語言來撰寫來發揮最大的效益;相同的,在數年後,Martin Fowler也針對更後端的資料庫技術,提出了「Polyglot Persistence」,用以打破SQL與NoSQL的界限以混合不同的資料庫系統,使用不同的工具到不同的目的;畢竟各式不同的資料庫原本就是設計用來解決不同的問題,若使用單一資料庫引擎來完成所有的需求通常會導致沒有效率的結果。

    以下的內容摘譯自2013年Martin Fowler於ThoughtWorks發表的簡報,原文位於:http://martinfowler.com/articles/nosql-intro-original.pdf


未來不應肓目的追求NoSQL
而是多語言堅持(Polyglot Persistence)

  1. 關聯式資料庫+SQL:


關聯式資料庫+SQL已經統領了企業界的資料庫系統數十年了,大家樂於使用的原因是

安全穩定的儲存資料
    把資料存儲在硬碟中,允許程式或系統透過最標準化的語言SQL來對資料進行各種操作與查詢。

應用程式整合
    企業內部的應用桯式都需要共用及分享資訊,為了確保所有的程式都能存取資料庫取得所需的資訊,SQL能夠確保資料庫中儲存的資訊都是最新且能持續的提供服務。

標準化
    關聯式資料庫以及標準的SQL語法已經廣泛的被應用及瞭解,IT業界對於此類的資料庫系統相當熟稔依賴,不需要去學習新的資料庫系統。

資料一致性
    很多使用者可能會在同時間內存取相同的資料,因此關聯式資料庫提供了「交易」方式來確保資料的一致性。

報表呈現
    SQL的簡單資料模型以及標準化,使得各種匯總產生報表的工具皆使用SQL作為操作的基本工具。

以上這些傳統上原本由DBA(資料庫管理者)所負責的項目與特色,在NoSQL的世界裏,則附加在各自擁有不同特色的NoSQL之中。

  1. SQL唯一霸主的地位正動揺







同一個cluster中有這麼多台的主機,每一部主機都有不確定的因素(當機的風險),但因為同時間可提供服務的主機不止一台,所以整體的cluster是可信賴的,因為其它運作中的機器會cover當掉的機器。

目前流行的雲端技術就相當依賴scale horizontally,換而言之,傳統關聯式資料庫則無法適用於雲端的場合,唯有透過web services的方式,讓不同的程式選擇它們自已合適的資料庫,才能提供有效率的資料庫整合存取。

Google和Amazon是最早因需求而採用此類大型Cluster的廠商,所以它們不約而同都避免使用傳統關聯式資料庫,這種作法,給予了當時正萌芽的NoSQL社群相當大的鼓舞。

  1. 我們現在有了NoSQL

NoSQL這個名稱並沒有一個標準的解釋,最早是在2009年的一個研討會上出現,但更多的爭論主要在於「什麼類型的資料庫才能稱為NoSQL?」,因此,只要符合下列條件之一,我們便可稱它為NoSQL資料庫:

        • 不使用傳統的關聯式資料庫和SQL語法
        • 被設計適合用於cluster的環境
        • Open Source
        • 沒有固定的schema,允許儲存任何型式的資料

Google的Bigtable以及Amazon的SimpleDB運作於他們自己的雲端服務上,這兩種也屬於NoSQL資料庫。

  1. 透過NoSQL,我們可以

    • 降低開發階段的包袱
大部份開發階段的時間會花在關聯性資料庫的設計工作上,雖然透過一些物件或UML等開發工具可以減少loading,但針對資料庫的開發設計上還是需要大量的時間;事實上,我們可以根據不同的開發需求來選擇適合的資料庫來降低開發階段花在資料庫的時間;目前大部份專案仍使用傳統資料庫的原因在於開始時就沒有選擇資料庫的念頭,預設就是使用關聯資料庫,而不是基於最好的選擇,導致必須花更多的成本、開發時間、以及執行效能,以及用不到的功能。

    • 橫向擴展資料庫的能力
NoSQL可利用擴充主機的方式,來橫向擴展整體資料庫的空間(大至Petabytes等級)與運算能力,以因應龐大的資料量。


Example:

Guardian(英國衛報 guardian.co.uk)
在新的功能中採用了相當多的MongoDB NoSQL,他們發現MongoDB的文件儲存模式對於內部程式在進行資料交換時更為方便。
  • 降低開發階段的包袱

DNC(民主黨全國委員會)
若使用傳統關聯資料庫,要在超過三億投票人的資訊中搜尋某個人的地址、email、電是相當痛苦的等待,因此DNC使用MongoDB作為其投票人的資料儲存方式。
  • 降低開發階段的包袱 與 橫向擴展資料庫

Danish Health Care(丹麥的健保系統)
    原本所有的藥品處方資料都集中儲存於mySQL資料庫,但有鑑於資料庫的可用性和查詢回應時間,已將資料轉移至Riak資料庫。
  • 橫向擴展資料庫

McLaren(英國麥克拉倫集團,以方程式賽車知名)
    將每場賽車產生的大量資訊記錄至MongoDB以提供後端查詢分析
  • 橫向擴展資料庫

  1. 傳統SQL仍有存在價值


    • 部份功能非傳統的關聯式資料庫不可
相當多的資料類型仍適合使用表格模式的關聯式資料庫,特別是在僅需要提取一部份的數據並且根據不同的目的整合成不同的格式的情況下。

    • ACID類型的交易:
ACID指的是atomicity, consistency, isolation及durability這四大特性,SQL著重於滿足此四個特性,但NoSQL為了滿足橫向擴充的能力,對於ACID無法充分的滿足。

    • 第三方工具程式的支援
長期以來SQL支配著資料庫的使用,因此擁有大量的工具軟體可供使用,但其它種類的資料庫可使用的軟體則相當的少。

    • IT人員的熟稔度
NoSQL對於大部份的資訊人員來說還算是新的資料庫,一般都還不太熟悉它,因此也會避免在專案中使用此類陌生的技術。


  1. Polyglot Persistence多語言堅持


Polyglot Persistence就是同時併用不同的資料儲存技術,依據不同的程式需求來選擇適合的資料庫。例如,若有更合適的選擇,我們有需要在關聯式資料庫中儲存位元類型的圖片檔案嗎?因此,Polyglot Persistence可應用在系統中的多個不同程式,也可以應用在單一程式中需針對不同資料而進行的處理及儲存



下表是一個Web application假設性的案例,針對不同的用途而使用不同資料庫系統




  1. 導入Polyglot Persistence會面臨的挑戰

    • 決策
我們將面臨何種情境需要使用那類型的資料庫,而非與往常一樣僅使用關聯式資料庫。

    • 新技術的支援
NoSQL屬於新的技術,相關的支援與工具也不多,在導入時肯定會面臨到使用新技術的不熟悉、抗拒及無法避免的陷阱等等困境。

    • 組織變革
如果組織或專案中已有資料相關部門,那麼導入對他們的影響性為何?

    • 資料最終一致性的處理
NoSQL無法提供與關聯資料庫一樣的交易性資料變動,因此,如何來處理過時的資料?或者要如何來讓不同的系統之間讀取到一致性的資料?

  1. 那些類型的專案適合Polyglot Persistence?







  1. 結語


  隨著雲端技術的峰起雲湧,各類型的資料庫技術也隨之竄起,以因應各種不同使用情境的需求,以往企業獨尊SQL關聯式資料庫的情況已不適用於目前多樣化的環境。

現今企業面對的,是更為複雜的資訊環璄需求與眾多可供選擇的資料庫技術,傳統上僅使用單一資料庫技術的觀念將無法勝任如此變動的大環境,因此SQL傳統關聯式資料庫與MySQL之間的藩籬應被打破,企業針對不同的需求來選擇適合的資料庫技術,將不同的技術揉合在同一套系統中,這也是資訊部門需要因應環境改變而調整並進化的例子。

2014年4月2日 星期三

NoSQL與MongoDB

近年來由於社群網站和雲端技術的興起,讓大量原本屬於資訊接收的使用者,變成資訊的產生和提供者,也因此促成了巨量資料存取以及資料庫動態擴展的強烈需求;部份為了資料量龐大、無法及時擴充資料庫而若惱的業者,紛紛捨棄了傳統的關聯式資料庫而改採分散式非關聯式資料庫來解決這些巨量資料的問題,此類型的資料庫,即目前相當流行的NoSQL資料庫;在此需求所帶動的潮流下,各種不同形態的NoSQL資料庫如雨後春筍般竄起,http://nosql-database.org/的表格中,就洋洋灑灑的列出了目前已知的NoSQL種類(已經達到150種),其中,MongoDB是眾多noSQL資料庫軟體中較為人熟知的一種,在四大類型(註)noSQL資料庫中,它是屬於最容易使用與上手的文檔類型資料庫。

  1. NoSQL種類

最早NoSQL的確是指No SQL這個意思,可回溯至90年代某個開放原始碼的關聯式資料庫 [Strozzi NoSQL],這個資料庫系統號稱不使用SQL作為查詢語言,而是使用Shell script來維護資料庫,因此一般人認為NoSQL應該是指No SQL,以代表它們不支援SQL這種結構性查詢語法,但近來則普遍將NoSQL視為「Not Only SQL」,即不只是SQL的意思,希望揉合SQL優點並混用關聯式資料庫和NoSQL資料庫來達成最佳的儲存效果。

一般我們將NoSQL依其資料模型分類為「Key-value Stores」、「Document Databases」、「Graph Databases」三種,也有第人提出第四種形態「Column Family Store」,或翻譯為「列族」或「欄位群」(個人認為這個比較貼切),它屬於一種「Key-value類型」的變形,適合用於大量的數據資料分佈在相當多台機器的情況。

但這種分類方式雖然有用但較粗糙,像「Key-value Stores」與「Document Databases」之間的界線是相當模糊的,因為在每個Key對應的Value值也可以塞入Document型態的內容,因此許多資料庫並不單純僅僅屬於某種型態種類,例如以OrientDB來說,它自稱是文件資料庫也是圖形資料庫;但基本上「Key-value Stores」、「Document Databases」、「Column Family Store」這三種有著同通的特性,我們都稱它們是屬於聚集導向,亦即,每個聚集都擁有一個鍵(key)來取得資料。



1. Key-values Stores 鍵值資料庫
它是利用一種雜湊表(hash table)的概念,使用一個唯一的鍵值(Key value)指向一個特定的值或資料,格式如同「Key:Value」;這種形態的資料庫相當簡單且容易導入使用,但若僅僅用於單純查詢或更新某個值的話,其效率比不上傳統的關連式資料庫。


Key-values Stores範例:
BerkeleyDB、LevelDB、Memcached、Project Voldemort、Redis、Riak

2. Column Family Stores 欄位群
這種形態的資料庫適用於大量資料分佈於很多台主機的情況,也是採用Key-values 的雜湊方式,但每個Key值會對映到不同的columns,而非單一的value。
http://www.tim-wellhausen.de/papers/NoSQL-Patterns/NoSQL-Patterns_html_m366c28ea.png

Column Family Stores範例:
Amazon SimpleDB、Cassandra、HBase、Hypertable

3. Document Databases 文件資料庫
文件儲存概念最早為Lotus Notes所採用,NoSQL也導入了此種方式,但實際上,NoSQL中所謂的Document Databases並非我們認知的實體文字檔案,而是一種資料表示方式(如json);如同Key-Value Stores般,Document Databases的每組Key所對應的value內容就是json資料表示方式的內容,而非一般人以為的實體文檔。

所以Document Databases可以作到巢狀的方式,在每個Value中又可定義為另一組Key:Value (如 {Key: {Key:Value, Key:Value} } ),使得Document Databases進行查詢時比起單純的Key-Value Stores更具效率。
Column Family Stores範例:
CouchDB、MongoDB、OrientDB、RavenDB、Terrastore

4. Graph Databases 圖學資料庫
依字面意義,我們會很容易誤解Graph Databases是針對圖片的資料庫檢索,但實際不然,Graph指的是每個資料節點之間的架構及關係;一般而言Graph Databases並沒有標準的作法,最基本包括了節點(Node)、關係(Relation)和屬性(Property)三種結構;若繪製出來可能會如同樹狀或網狀般不同的格式,因此Graph Databases相當適用用於地理圖資或者朋友、親屬、社交等系統,此類資料庫最大的特性是對複雜性的擴充力,關係越複雜的資料越適合使用圖學資料庫。
  http://i.msdn.microsoft.com/dynimg/IC676153.png

Graph Databases範例:
FlockDB、HyperGraphDB、Infinite Graph、Neo4J、OrinetDB

  1. 安裝MongoDB

MongoDB屬於Document Databases的NoSQL開源軟體,它支援三種的平台:WindwsLinux與Mac OS,本文以Windows 7平台作為操作及示範,另需注意的是,自2.2版的MongoDB開始,已不再支援XP系統,此外,如果是Server 2008 R2或者是Windows 7的系統,需要額外先安裝一個微軟的hotfix KB2731284。http://support.microsoft.com/kb/2731284

    1. 下載:先到http://www.mongodb.org/downloads 選擇適合的版本(64/32 bits)
    2. 安裝:解壓縮下載後的檔案,移至適當地方並重新命名。(MongoDB是一支標準的綠色程式,不需安裝即可在任何路徑下直接執行)
cd \
move C:\mongodb-win32-* C:\mongodb
    1. 路徑:MongoDB需要一個資料夾來存放檔案,預設是C:\data\db,您也可以自行建立其它的目錄,並在啟動MongoDB時用—dbpath參數來指定。
    2. 啟動:要啟動MongoDB很簡單,只要執行mongod.exe就行了。
C:\mongodb\bin\mongod.exe
我們會看到waiting for connections的訊息出現在視窗中,這表示MongoDB已經成功的在運作了。
如果DB資料夾需另外指定:
C:\mongodb\bin\mongod.exe --dbpath d:\test\mongodb\data

如果DB資料夾路徑中有空白字元:
    C:\mongodb\bin\mongod.exe --dbpath "d:\test\mongo db data"

    1. 測試:開啟另一個DOS視窗,執行 C:\mongodb\bin\mongo.exe
Mongo.exe預設會連到本機的27017 port(mongod listen的port),此時,你可以觀察到mongod回應的connection accepted訊息。




  1. MongoDB資料庫架構



Document:
MongoDB中的Document,是由一串key:value形態組合而成的Json格式字串,它類似於關連式資料庫的Record;其中的key,等同於關連式資料庫Record中的欄位,而value,則類似關連式資料庫欄位的值;但MongoDB Document的key:value是動態性,可以隨意增減的,而傳統關連式資料庫的欄位則無法隨意更改,一更改便會影響到該table全部的records。

例如,下方的json字串可視為一個document
        {
        user:'user2',
        message: 'My second comments',
        dateCreated: new Date(2011,1,25,7,45),
        like: 5
         }

Collection:
    Collection是一群Documents的集合,它相當類似於關連式資料庫中所謂的table;每個Collection只會屬於一個資料庫,需注意的是,MongoDB的Collection結構是動態的,意謂著相同Collection下的每個Document可以擁有不同的key值定義,這點與關連式資料庫不同,關連式資料庫中table的每筆Record皆擁有相同的欄位定義,不會有欄位相異的情況。

Database:
    與關連式資料中,每個Database會有好幾個tables一樣,Collections會屬於某一個Database,從檔案系統的角度來看,每個Database會有數個檔案(視其擁有幾個Collections而定)。



  1. 瞭解JSON格式:

JSON為JavaScript Object Notation的縮寫,與XML一樣,屬於資料交換用途的一種檔案格式,但它比起XML,具有更易於閱讀、更精簡、檔案更小、更方便機器解析和生成等特性,因此,近年來在一些需要快速傳輸的WEB領域,已逐漸取代XML成為資料交換的標準;此外,一些NoSQL資料庫也應用JSON成為其儲存格式的標準(如MongoDB、RavenDB以及CouchDB等)。

JSON的檔案格式:
  1. 以"{"開始,以"}"結尾
  2. 每個KEY與Value中間用":"分隔
  3. 若該KEY的Value不止一個,則用”,”分隔每個Value,最後用"[" 與 "]"包圍。
  4. 不同的"KEY:Value"之間用","分隔
   
{                    以"{"開始,以"}"結尾
     'familys' = [          familys collection有二筆documents,各分別有二組Key:Value,
       {'name' : 'Bruce',     用”,”分隔每個Value,最後用"[" 與 "]"包圍(表示為陣列)。
        'age' : 18,
        'sex' : 'male'},    每筆document資料之間用","分隔
       {'name' : 'Sherry',
        'age' : 16,
        'sex' : 'famale'}
     ]
}



上面的json內容,表示在familys這個collection下,有兩筆資料如下:
names
age
sex
Bruce
18
male
Sherry
16
female



  1. 基本操作

A) 顯示目前使用的DB:啟動MongoDB時,預設開啟的資料庫是Test,輸入db指令,可以顯示目前使用中的資料庫。
> db
Test

    B) 顯示目前所有的資料庫以及使用的大小:
> show dbs
local   0.078125GB
test    0.203125GB

    C) 切換到另一個資料庫local
> use local
switched to db local
> db
local

    D) 輸入help可顯示所有的指令及說明
 > help

    E) 建立新資料庫
        直接使用use {DBNAME},若沒有該資料庫,則會自動新增。
 > use mydb
switched to db mydb

    F) 新增資料到資料庫
    我們先定義j與k的內容,再利用db.testData.insert新增到資料庫中;請注意,db.testData.insert( j )中的testData,代表collection的名稱。
 > j = { name : "mongo" }
 { "name" : "mongo" }
 > k = { x : 3 }
 { "x" : 3 }
  > db.testData.insert( j )
  > db.testData.insert( k )
   
    您會發現,在mongoDB的模式下也可以執行javascript的命令,這對於我們在處理相關資料庫操作上,是相當方便的。

    G) 顯示資料庫中的collections列表(system.indexes預設每個DB都會有)
  > show collections
system.indexes
testData

    H) 使用find()可列出該collection的所有文件
> db.testData.find()
{ "_id" : ObjectId("53392bd6b19ffea4731632cd"), "name" : "mongo" }
{ "_id" : ObjectId("53392bddb19ffea4731632ce"), "x" : 3 }

MongoDB會自動為每筆文件附加上唯一的欄位_id。

    I) 以陣列方式來使用:hasNext()與next()
    hasNext()傳回true如果下一個指標有document,next()會傳回下一個document。
    > var c = db.testData.find()
    > while ( c.hasNext() ) printjson( c.next() )
{ "_id" : ObjectId("53392bd6b19ffea4731632cd"), "name" : "mongo" }
{ "_id" : ObjectId("53392bddb19ffea4731632ce"), "x" : 3 }

    在上例中,db.testData.find()所回傳的列表會放在c變數中,我們再利用hasNext()與next()來操控c變數中的資料,其中,printjson()此函數是將json格式的資料顯示出來,此外,    也可以使用printjson(c[1]) 的方式直接指定第幾筆資料。

    J) 搜尋特定文件:find()與findOne()
        我們要搜尋文件中X值為3的資料,可以用 { x:3 }
> db.testData.find( {x:3} )
{ "_id" : ObjectId("53392bddb19ffea4731632ce"), "x" : 3 }

    如果我們只需要傳回一筆文件,可以用findOne()取代find()
> db.testData.findOne( {x:3} )
{ "_id" : ObjectId("53392bddb19ffea4731632ce"), "x" : 3 }

如果在find後方接limit(),則可以限制回傳的筆數
> db.testData.find().limit(2)
{ "_id" : ObjectId("53392bd6b19ffea4731632cd"), "name" : "mongo" }
{ "_id" : ObjectId("53392bddb19ffea4731632ce"), "x" : 3 }
> db.testData.find().limit(1)
{ "_id" : ObjectId("53392bd6b19ffea4731632cd"), "name" : "mongo" }


  1. 結語

以上簡要介紹了NoSQL以及MongoDB的安裝和基本操作,會發現MongoDB相當容易安裝且它的查詢語言並不複雜;一般初接觸NoSQL,可以會誤以為可以用NoSQL來取代傳統的資料庫如MSSQLOracle或mySQL等,但其實兩者各有其應用範圍,如果系統對於資料即時且正確的要求性相當高,例如訂單、付款、會員資料等交易性資料,那麼必須優先考慮傳統關聯性資料庫不可,但關聯性資料庫的最大缺陷在於擴展性不夠,雖然各個資料庫廠家都有cluster的解決方案,但擴展性都十分有限,因此,如果您的資料量龐大隨時都可能需要增加資料庫的空間,且對資料的正確與即時性能接受短時間不正確並最終會正確(例如Facebook按讚的數量統計),那麼NoSQL會是適合的選擇。