NoSQL架構實踐

常常有朋友遇到困惑,看到NoSQL的介紹,以爲很好,可是殊不知道如何正式用到本身的項目中。很大的緣由就是思惟固定在MySQL中了,他們問得最多的問題就是用了NoSQL,我如何作關係查詢。那麼接下來,咱們看下怎麼樣在咱們的系統中使用NoSQL。php

怎麼樣把NoSQL引入到咱們的系統架構設計中,須要根據咱們系統的業務場景來分析,什麼樣類型的數據適合存儲在NoSQL數據庫中,什麼樣類型的數據必須使用關係數據庫存儲。明確引入的NoSQL數據庫帶給系統的做用,它能解決什麼問題,以及可能帶來的新的問題。下面咱們分析幾種常見的NoSQL架構。html

(一)NoSQL做爲鏡像

不改變原有的以MySQL做爲存儲的架構,使用NoSQL做爲輔助鏡像存儲,用NoSQL的優點輔助提高性能。前端

圖 1 -NoSQL爲鏡像(代碼完成模式 )mysql

//寫入數據的示例僞代碼 

//data爲咱們要存儲的數據對象 
data.title=」title」; 
data.name=」name」; 
data.time=」2009-12-01 10:10:01」; 
data.from=」1」; 
id=DB.Insert(data);//寫入MySQL數據庫 
NoSQL.Add(id,data);//以寫入MySQL產生的自增id爲主鍵寫入NoSQL數據庫

若是有數據一致性要求,能夠像以下的方式使用web

//寫入數據的示例僞代碼 
//data爲咱們要存儲的數據對象 
bool status=false; 
DB.startTransaction();//開始事務 
id=DB.Insert(data);//寫入MySQL數據庫 
if(id>0){ 
    status=NoSQL.Add(id,data);//以寫入MySQL產生的自增id爲主鍵寫入NoSQL數據庫 
} 
if(id>0 && status==true){ 
    DB.commit();//提交事務 
}else{ 
    DB.rollback();//不成功,進行回滾 
}

上面的代碼看起來可能以爲有點麻煩,可是隻須要在DB類或者ORM層作一個統一的封裝,就能實現重用了,其餘代碼都不用作任何的修改。sql

這種架構在原有基於MySQL數據庫的架構上增長了一層輔助的NoSQL存儲,代碼量不大,技術難度小,卻在可擴展性和性能上起到了很是大的做用。只須要程序在寫入MySQL數據庫後,同時寫入到NoSQL數據庫,讓MySQL和NoSQL擁有相同的鏡像數據,在某些能夠根據主鍵查詢的地方,使用高效的NoSQL數據庫查詢,這樣就節省了MySQL的查詢,用NoSQL的高性能來抵擋這些查詢。mongodb

圖 2 -NoSQL爲鏡像(同步模式)數據庫

這種不經過程序代碼,而是經過MySQL把數據同步到NoSQL中,這種模式是上面一種的變體,是一種對寫入透明可是具備更高技術難度一種模式。這種模式適用於現有的比較複雜的老系統,經過修改代碼不易實現,可能引發新的問題。同時也適用於須要把數據同步到多種類型的存儲中。json

MySQL到NoSQL同步的實現可使用MySQL UDF函數,MySQL binlog的解析來實現。能夠利用現有的開源項目來實現,好比:後端

有了這兩個MySQL UDF函數庫,咱們就能經過MySQL透明的處理Memcached或者Http協議,這樣只要有兼容Memcached或者Http協議的NoSQL數據庫,那麼咱們就能經過MySQL去操做以進行同步數據。再結合lib_mysqludf_json,經過UDF和MySQL觸發器功能的結合,就能夠實現數據的自動同步。

(二)MySQL和NoSQL組合

MySQL中只存儲須要查詢的小字段,NoSQL存儲全部數據。

圖 3 -MySQL和NoSQL組合

//寫入數據的示例僞代碼 

//data爲咱們要存儲的數據對象 
data.title=」title」; 
data.name=」name」; 
data.time=」2009-12-01 10:10:01」;
data.from=」1」;
bool status=false; 
DB.startTransaction();//開始事務 
id=DB.Insert(「INSERT INTO table (from) VALUES(data.from)」);//寫入MySQL數據庫,只寫from須要where查詢的字段 
if(id>0){ 
    status=NoSQL.Add(id,data);//以寫入MySQL產生的自增id爲主鍵寫入NoSQL數據庫 
} 
if(id>0 && status==true){ 
    DB.commit();//提交事務 
}else{ 
    DB.rollback();//不成功,進行回滾 
}

把須要查詢的字段,通常都是數字,時間等類型的小字段存儲於MySQL中,根據查詢創建相應的索引,其餘不須要的字段,包括大文本字段都存儲在NoSQL中。在查詢的時候,咱們先從MySQL中查詢出數據的主鍵,而後從NoSQL中直接取出對應的數據便可。

這種架構模式把MySQL和NoSQL的做用進行了融合,各司其職,讓MySQL專門負責處理擅長的關係存儲,NoSQL做爲數據的存儲。它有如下優勢:

  • 節省MySQL的IO開銷。因爲MySQL只存儲須要查詢的小字段,再也不負責存儲大文本字段,這樣就能夠節省MySQL存儲的空間開銷,從而節省MySQL的磁盤IO。咱們曾經經過這種優化,把MySQL一個40G的表縮減到幾百M。
  • 提升MySQl Query Cache緩存命中率。咱們知道query cache緩存失效是表級的,在MySQL表一旦被更新就會失效,通過這種字段的分離,更新的字段若是不是存儲在MySQL中,那麼對query cache就沒有任何影響。而NoSQL的Cache每每都是行級別的,只對更新的記錄的緩存失效。
  • 提高MySQL主從同步效率。因爲MySQL存儲空間的減少,同步的數據記錄也減少了,而部分數據的更新落在NoSQL而不是MySQL,這樣也減小了MySQL數據須要同步的次數。
  • 提升MySQL數據備份和恢復的速度。因爲MySQL數據庫存儲的數據的減少,很容易看到數據備份和恢復的速度也將極大的提升。
  • 比之前更容易擴展。NoSQL天生就容易擴展。通過這種優化,MySQL性能也獲得提升。

好比手機鳳凰網就是這種架構 http://www.cnblogs.com/sunli/archive/2010/12/20/imcp.html

 



(三)純NoSQL架構

只使用NoSQL做爲數據存儲。

圖 4-純NoSQL架構

在一些數據結構、查詢關係很是簡單的系統中,咱們能夠只使用NoSQL便可以解決存儲問題。這樣不但能夠提升性能,還很是易於擴展。手機鳳凰網的前端展現系統就使用了這種方案。

在一些數據庫結構常常變化,數據結構不定的系統中,就很是適合使用NoSQL來存儲。好比監控系統中的監控信息的存儲,可能每種類型的監控信息都不太同樣。這樣能夠避免常常對MySQL進行表結構調整,增長字段帶來的性能問題。

這種架構的缺點就是數據直接存儲在NoSQL中,不能作關係數據庫的複雜查詢,若是因爲需求變動,須要進行某些查詢,可能沒法知足,因此採用這種架構的時候須要確認將來是否會進行復雜關係查詢以及如何應對。

很是幸運的是,有些NoSQL數據庫已經具備部分關係數據庫的關係查詢特性,他們的功能介於key-value和關係數據庫之間,卻具備key-value數據庫的性能,基本能知足絕大部分web 2.0網站的查詢需求。好比:

MongoDB就帶有關係查詢的功能,能解決經常使用的關係查詢,因此也是一種很是不錯的選擇。下面是一些MongoDB的資料:

雖然Foursquare使用MongoDB的宕機事件的出現令人對MongoDB的自動Shard提出了質疑,可是毫無疑問,MongoDB在NoSQL中,是一個優秀的數據庫,其單機性能和功能確實是很是吸引人的。因爲上面的例子有詳細的介紹,本文就不作MongoDB的使用介紹。

Tokyo Tyrant數據庫帶有一個名爲table的存儲類型,能夠對存儲的數據進行關係查詢和檢索。一個table庫相似於MySQL中的一個表。下面咱們看一個小演示:

咱們要存儲一批用戶信息,用戶信息包含用戶名(name),年齡(age),email,最後訪問時間(lastvisit),地區(area)。下面爲寫入的演示代碼:

<? php
$tt = new TokyoTyrantTable ( " 127.0.0.1 " , 1978 );
$tt -> vanish (); // 清空
$id = $tt -> genUid (); // 獲取一個自增id
//put方法提供數據寫入。 put ( string $key , array $columns );

$tt -> put ( $id , array ( " id " => $id , " name " => " zhangsan " , " age " => 27 , " email " => " zhangsan@gmail.com " , " lastvisit " => strtotime ( " 2011-3-5 12:30:00 " ) , " area " => " 北京 " ) );
$id = $tt -> genUid ();
$tt -> put ( $id , array ( " id " => $id , " name " => " lisi " , " age " => 25 , " email " => " lisi@126.com " , " lastvisit " => strtotime ( " 2011-3-3 14:40:44 " ) , " area " => " 北京 " ) );
$id = $tt -> genUid ();
$tt -> put ( $id , array ( " id " => $id , " name " => " laowang " , " age " => 37 , " email " => " laowang@yahoo.com " , " lastvisit " => strtotime ( " 2011-3-5 08:30:12 " ) , " area " => " 成都 " ) );
$id = $tt -> genUid ();
$tt -> put ( $id , array ( " id " => $id , " name " => " tom " , " age " => 21 , " email " => " tom@hotmail.com " , " lastvisit " => strtotime ( " 2010-12-10 13:12:13 " ) , " area " => " 天津 " ) );
$id = $tt -> genUid ();
$tt -> put ( $id , array ( " id " => $id , " name " => " jack " , " age " => 21 , " email " => " jack@gmail.com " , " lastvisit " => strtotime ( " 2011-02-24 20:12:55 " ) , " area " => " 天津 " ) );
// 循環打印數據庫的全部數據庫
$it = $tt -> getIterator ();
foreach ( $it as $k => $v ) {
print_r ( $v );
}
?>

好比咱們須要查詢年齡爲21歲的全部用戶:

<? php
$tt = new TokyoTyrantTable ( " 127.0.0.1 " , 1978 );
$query = $tt -> getQuery ();
// 查詢年齡爲21歲的用戶
$query -> addCond ( 「age」 , TokyoTyrant :: RDBQC_NUMEQ , 21 」 );
print_r ( $query -> search () );
?>

查詢全部在2011年3月5日以後登錄的用戶:

<? php
$tt = new TokyoTyrantTable ( " 127.0.0.1 " , 1978 );
$query = $tt -> getQuery ();
$query -> addCond ( 「lastvisit」 , TokyoTyrant :: RDBQC_NUMGE , strtotime ( " 2011-3-5 00:00:00 " ) );
print_r ( $query -> search () );
?>

從上面的示例代碼能夠看出,使用起來是很是簡單的,甚至比SQL語句還要簡單。Tokyo Tyrant的表類型存儲還提供了給字段創建普通索引和倒排全文索引,大大加強了其檢索功能和檢索的性能。

因此,徹底用NoSQL來構建部分系統,是徹底可能的。配合部分帶有關係查詢功能的NoSQL,在開發上比MySQL數據庫更加快速和高效。

(四)以NoSQL爲數據源的架構

數據直接寫入NoSQL,再經過NoSQL同步協議複製到其餘存儲。根據應用的邏輯來決定去相應的存儲獲取數據。

圖 5 -以NoSQL爲數據源

純NoSQL的架構雖然結構簡單,易於開發,可是在應付需求的變動、穩定性和可靠性上,老是給開發人員一種風險難於控制的感受。爲了下降風險,系統的功能不侷限在NoSQL的簡單功能上,咱們可使用以NoSQL爲數據源的架構。

在這種架構中,應用程序只負責把數據直接寫入到NoSQL數據庫就OK,而後經過NoSQL的複製協議,把NoSQL數據的每次寫入,更新,刪除操做都複製到MySQL數據庫中。同 時,也能夠經過複製協議把數據同步複製到全文檢索實現強大的檢索功能。在海量數據下面,咱們也能夠根據不一樣的規則,把數據同步複製到設計好的分表分庫的 MySQL中。這種架構:

  • 很是靈活。能夠很是方便的在線上系統運行過程當中進行數據的調整,好比調整分庫分表的規則、要添加一種新的存儲類型等等。
  • 操做簡單。只須要寫入NoSQL數據庫源,應用程序就不用管了。須要增長存儲類型或者調整存儲規則的時候,只須要增長同步的數據存儲,調整同步規則便可,無需更改應用程序的代碼。
  • 性能高。數據的寫入和更新直接操做NoSQL,實現了寫的高性能。而經過同步協議,把數據複製到各類適合查詢類型的存儲中(按照業務邏輯區分不一樣的存儲),能實現查詢的高性能,不像之前MySQL一種數據庫就全包了。或者就一個表負責跟這個表相關的全部的查詢,如今能夠把一個表的數據複製到各類存儲,讓各類存儲用本身的長處來對外服務。
  • 易擴展。開發人員只須要關心寫入NoSQL數據庫。數據的擴展能夠方便的在後端由複製協議根據規則來完成。

這種架構須要考慮數據複製的延遲問題,這跟使用MySQL的master-salve模式的延遲問題是同樣的,解決方法也同樣。

在這種以NoSQL爲數據源的架構中,最核心的就是NoSQL數據庫的複製功能的實現。而當前的幾乎全部的NoSQL都沒有提供比較易於使用的複製接口來完成這種架構,對NoSQL進行復制協議的二次開發,須要更高的技術水平,因此這種架構看起來很好,可是卻不是很是容易實現的。個人開源項目PHPBuffer中有個實現TokyoTyrant複製的例子,雖然是PHP版本的,可是很容易就能夠翻譯成其餘語言。經過這個例子的代碼,能夠實現從Tokyo Tyrant實時的複製數據到其餘系統中。

總結

以NoSQL爲主的架構應該算是對NoSQL的一種深度應用,整個系統的架構以及代碼都不是很複雜,可是卻須要必定的NoSQL使用經驗才行。

相關文章
相關標籤/搜索