“NOSQL” 雜談


引言:

 nosql 的興起和革命,在我看來已經開始逐漸影響到了傳統的sql的地位,但是僅僅是影響而已,取代是不太可能的。

 

 


 

正文:

兩年前,一個偶然的機會開始接觸到 nosql ( mongodb )。用來作數據挖掘的存儲容器,第一次接觸到nosql,真的被它驚艷到了。鄙人受到傳統的SQL的思維定勢,甚至一時間難以接受。

mongodb是一個非關系型文檔數據庫,非常適合文檔類型的數據的存儲,查詢也十分方便,支持動態的橫向和縱向的數據擴展。愛不釋手。下個用幾行shell來展示一下mongodb的魅力

 

 

show dbs;

//無則會創建
use mydb;


show collections;


//新建集合,相當於mysql中的表
db.createCollection('mycollection');

//插入數據.
db.mycollection.insert({'username':'chenqimiao','age':23})

db.mycollection.insert({'username':'cqm','age':23,'sex':'男'})

//查詢
db.mycollection.find({"age":23});

 

 

類比mysql的話,最大的區別可能在於表結構或者說集合結構的定義了,mysql的列是預定義的,而mongodb是在插入數據的時候才確定這條數據的列。

 

所以mongodb可以支持動態擴展列,在mongo中可能不叫列,要叫作域(field)吧。

 

 


 

 

后來花了一點時間了解了一下 HBase , Habase   誕生於 Hadoop的子項目,受大數據的遺傳。單表可以非常的大,同樣Habase也是基於列的。

 

 

印象中HBase 中有一個非常特別的特性,HBase 的數據覆蓋,並不是實際的覆蓋。HBase 有一個時間維度的概念,所有的數據都是基於這個維度進行存儲的,

簡單點說,同樣的key  可能在HBase 中存在兩個value。它是怎么做到的?因為它給每一條記錄都偷偷記錄了一個timestamp,每次你去覆蓋鍵值對的時候,你以為你已經刪除了舊值,替換成了新

值,而實際上只是再添加了一條記錄而已,兩條記錄共存在一個時間維度上。每次get(key)默認取最新的一個value,僅此而已。

 

你還可以手動設置失效時間TTL,這樣每一個值就會有一個有效期,過了有效期都值是不能get出來了,但是值還是存儲在HBase中並未丟失。

 

 

 

 


 

 

 

再后來接觸到了 redis ,相信大家對這個都相當的熟悉,基於內存的緩存數據庫,簡單的set()  get()就可以了緩存一些經常使用到的值,之前我也專門介紹過 redis的安裝,shell命令,多實例部署,讀寫分離,主從復制,哨兵 等等問題。

 

 

 

當數據超過一定量 , redis會把數據swap到文件中去,如果使用到swap中的文件的值,redis會把文件在swap到內存中,進行讀寫,十分智能。支持的數據類型也比較多 ,除了k-v 還有hset ,hash  ,zset等等

 

 

 

 

 


 

 

 

最近在做在線課堂,用到了 Memcache ,這個東西基本和redis的使用場景相似,基於內存的nosql。但是支持的數據類型只有k-v的形式。這是不同於redis的一點。

其二的話, Memcache 不支持文件持久化。

其三, memcache 的多實例,是基於客戶端的,這個比較有意思要好好聊一聊了,象我們平常接觸的mysql,redis,多實例同步基本是讀寫分離,主從復制,主機寫,從機讀,這樣的模式。可以說是基於服務端的多實例方案。但是 memcache 有點好玩了,它的多實例之間不進行同步,那它是怎么做到負載均衡的時候保證數據的完整性呢?

 

說到這里,我想先介紹一下memcache的主流的客戶端程序(JAVA)

  • 官方提供的基於傳統阻塞io由Greg Whalin維護的客戶端  

     較早推出的客戶端,穩定,持久運行。

 

  • Dustin Sallings實現的基於java nio的Spymemcached  

    A simple, asynchronous, single-threaded memcached client written in java. 支持異步,單線程的memcached客戶端,用到了java1.5版本的concurrent和nio,存取速度會高於前者,但是穩定性不好,測試中常 報timeOut等相關異常。   

  • XMemcache

           Memcached同樣是基於java nio的客戶端,java nio相比於傳統阻塞io模型來說,有效率高(特別在高並發下)和資源耗費相對較少的優點。傳統阻塞IO為了提高效率,需要創建一定數量的連接形成連接 池,而nio僅需要一個連接即可(當然,nio也是可以做池化處理),相對來說減少了線程創建和切換的開銷,這一點在高並發下特別明顯。因此 XMemcached與Spymemcached在性能都非常優秀,在某些方面(存儲的數據比較小的情況下)Xmemcached比 Spymemcached的表現更為優秀,具體可以看這個Java Memcached Clients Benchmark。 

 

 

根據上面的介紹,大概可以了解到,官方提供的是阻塞的客戶端,要利用線程池來實現並發,但是官方提供的包還有一個非常致命的問題,不提供 CAS 的同步功能。

什么是 CAS ?

了解過java下面的 java.util.concurrent.atomic; 下面的類的同學應該知道, CAS (check and swap),這是一種樂觀鎖的實現,程序陷入一個循環,得到舊值,執行CAS方法,傳入舊值,新值,若舊值未發生變化 ,則用舊值,換出新值。

while(true){
    
     Object oldValue = atomicObject.get("key1") ;

     Object newValue = new Object();
  
     Object value  =   checkAndSwap("key1",oldValue,newValue) ;


     if(value!=null&&oldValue.equals(value))  

           break;
     
}

 

 

解釋完 CAS ,回到剛才的問題,為什么沒有實現 CAS ,是官方包一個致命的弱點,明白了 CAS 原理的同學,應該發現其實它就是一個同步的手段,那為什么不能使用 syncronized 的。那是因為數據並發發生在不同項目里面,沒有辦法給多個項目之前共用一個 syncronized 。這個時候只能利用memcache提供的鎖,利用 CAS 作為同步手段。

慶幸spyMemcache和xmemcache都提供了 CAS 的操作。

 

那么問題來了,剛剛最早提出的問題如何解答?( CAS 多實例同步問題。)其實啊memcache根本不需要進行多實例同步,它的多實例是依賴於客戶端程序實現的。

比如spymemcache:

 para.memcache.server=192.168.202.121:11211,192.168.202.121:11210 配置兩個server即可。其余的操作和操作一個memcache是一摸一樣的,客戶端會根據Hash散列算法( HashMap 的實現算法)將鍵值對放到對應的 memcache 中,由於算法一致性,所以存取雙方都能得知鍵值在哪一個 memcache 中。這樣就實現了多實例了。

 

 另外spymemcache客戶端還有一個比較厲害的地方,它能直接將java對象序列化,作為k-v中的v,get(k)的時候自動反序列化成對象,無需直接操作JSONObject,當然前提是對象實現

 Serializable 接口,並給定一個 serialVersionUID 。redis也可以直接存儲二進制文件,但是官方提供的客戶端程序,並沒有封裝好序列化和反序列化,需要自己實現。


 

 

說了這么多nosql,好像傳統sql無用武之地一般。其實傳統的sql才是最穩定的,適用面積最廣,最安全,並且提供了事務回滾,保證數據一致性,這是nosql為了提高速度,增強擴展性,所要面臨的一部分舍棄。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM