1. 程式人生 > >幾款主流 NoSQL 資料庫的對比

幾款主流 NoSQL 資料庫的對比

最近小組準備啟動一個 node 開源專案,從前端親和力、大資料下的IO效能、可擴充套件性幾點入手挑選了 NoSql 資料庫,但具體使用哪一款產品還需要做一次選型。

我們最終把選項範圍縮窄在 HBase、Redis、MongoDB、Couchbase、LevelDB 五款較主流的資料庫產品中,本文將主要對它們進行分析對比。

鑑於缺乏專案中的實戰經驗沉澱,本文內容和觀點主要還是從各平臺資料蒐羅彙總,也不會有太多深入或底層原理探討。

本文所引用的資料來源將示於本文尾部。所彙總的內容僅供參考,若有異議望指正。

HBase

HBase 是 Apache Hadoop 中的一個子專案,屬於 bigtable 的開源版本,所實現的語言為Java(故依賴 Java SDK)

。HBase 依託於 Hadoop 的 HDFS分散式檔案系統作為最基本儲存基礎單元。

HBase在列上實現了 BigTable 論文提到的壓縮演算法、記憶體操作和布隆過濾器。HBase的表能夠作為 MapReduce任務的輸入和輸出,可以通過Java API來訪問資料,也可以通過REST、Avro或者Thrift的API來訪問。
1. 特點
1.1 資料格式

HBash 的資料儲存是基於列(ColumnFamily)的,且非常鬆散—— 不同於傳統的關係型資料庫(RDBMS),HBase 允許表下某行某列值為空時不做任何儲存(也不佔位),減少了空間佔用也提高了讀效能。

不過鑑於其它NoSql資料庫也具有同樣靈活的資料儲存結構,該優勢在本次選型中並不出彩。
我們以一個簡單的例子來了解使用 RDBMS 和 HBase 各自的解決方式:

⑴ RDBMS方案:

其中Article表格式:

Author表格式:

⑵ 等價的HBase方案:

對於前端而言,這裡的 Column Keys 和 Column Family 可以看為這樣的關係:

1234567891011 columId1={//id=1的行article:{//ColumnFamily-articletitle:XXX,//ColumnFamily-article下的key之一content:XXX,tags:XXX},author:{//ColumnFamily-authorname:XXXnickname:XXX}}

1.2 效能

HStore儲存是HBase儲存的核心,它由兩部分組成,一部分是MemStore,一部分是StoreFiles。

MemStore 是 Sorted Memory Buffer,使用者寫入的資料首先會放入MemStore,當MemStore滿了以後會Flush成一個StoreFile(底層實現是HFile),當StoreFile檔案數量增長到一定閾值,會觸發Compact合併操作,將多個StoreFiles合併成一個StoreFile,合併過程中會進行版本合併和資料刪除,因此可以看出HBase其實只有增加資料,所有的更新和刪除操作都是在後續的compact過程中進行的,這使得使用者的寫操作只要進入記憶體中就可以立即返回,保證了HBase I/O的高效能。

1.3 資料版本

Hbase 還能直接檢索到往昔版本的資料,這意味著我們更新資料時,舊資料並沒有即時被清除,而是保留著:

Hbase 中通過 row+columns 所指定的一個存貯單元稱為cell。每個 cell都儲存著同一份資料的多個版本——版本通過時間戳來索引。

時間戳的型別是 64位整型。時間戳可以由Hbase(在資料寫入時自動 )賦值,此時時間戳是精確到毫秒的當前系統時間。時間戳也可以由客戶顯式賦值。如果應用程式要避免資料版本衝突,就必須自己生成具有唯一性的時間戳。每個 cell中,不同版本的資料按照時間倒序排序,即最新的資料排在最前面。

為了避免資料存在過多版本造成的的管理 (包括存貯和索引)負擔,Hbase提供了兩種資料版本回收方式。一是儲存資料的最後n個版本,二是儲存最近一段時間內的版本(比如最近七天)。使用者可以針對每個列族進行設定。

1.4 CAP類別
屬於CP型別瞭解更多

2. Node下的使用

HBase的相關操作可參考下表:

在node環境下,可通過 node-hbase 來實現相關訪問和操作,注意該工具包依賴於 PHYTHON2.X(3.X不支援)和Coffee。

如果是在 window 系統下還需依賴 .NET framwork2.0,64位系統可能無法直接通過安裝包安裝。

官方示例:

12345678910111213141516 varassert=require('assert');varhbase=require('hbase');hbase({host:'127.0.0.1',port:8080}).table('my_table')//建立一個Column Family.create('my_column_family',function(err,success){this.row('my_row')//定位到指定行.put('my_column_family:my_column','my value',function(err,success){this.get('my_column_family',function(err,cells){this.exists(function(err,exists){assert.ok(exists);});});});});

資料檢索:

12345678 client.table('node_table').scan({startRow:'my_row',//起始行maxVersions:1//版本},function(err,rows){console.log(err,rows);});

另有 hbase-client 也是一個不錯的選擇,具體API參照其文件。

3. 優缺點

優勢

1. 儲存容量大,一個表可以容納上億行,上百萬列;

2. 可通過版本進行檢索,能搜到所需的歷史版本資料;

3. 負載高時,可通過簡單的新增機器來實現水平切分擴充套件,跟Hadoop的無縫整合保障了其資料可靠性(HDFS)和海量資料分析的高效能(MapReduce)

4. 在第3點的基礎上可有效避免單點故障的發生。

缺點

1. 基於Java語言實現及Hadoop架構意味著其API更適用於Java專案;

2. node開發環境下所需依賴項較多、配置麻煩(或不知如何配置,如持久化配置),缺乏文件;

3. 佔用記憶體很大,且鑑於建立在為批量分析而優化的HDFS上,導致讀取效能不高;

4. API相比其它 NoSql 的相對笨拙。

適用場景

1. bigtable型別的資料儲存;

2. 對資料有版本查詢需求;

3. 應對超大資料量要求擴充套件簡單的需求。

Redis
Redis 是一個開源的使用ANSI C語言編寫、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。目前由VMware主持開發工作。
1. 特點

1.1 資料格式
Redis 通常被稱為資料結構伺服器,因為值(value)可以是 字串(String), 雜湊(Hash/Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)五種型別,操作非常方便。比如,如果你在做好友系統,檢視自己的好友關係,如果採用其他的key-value系統,則必須把對應的好友拼接成字串,然後在提取好友時,再把value進行解析,而redis則相對簡單,直接支援list的儲存(採用雙向連結串列或者壓縮連結串列的儲存方式)。
我們來看下這五種資料型別。

⑴ String

  • string 是 Redis 最基本的型別,你可以理解成與 Memcached 一模一樣的型別,一個key對應一個value。
  • string 型別是二進位制安全的。意思是 Redis 的 string 可以包含任何資料。比如 jpg 圖片或者序列化的物件 。
  • string 型別是 Redis 最基本的資料型別,一個鍵最大能儲存512MB。

例項:

1234 redis127.0.0.1:6379>SET name zfpxOKredis127.0.0.1:6379>GET name"zfpx"

在以上例項中我們使用了 Redis 的 SET 和 GET 命令。鍵為 name,對應的值為”zfpx”。 注意:一個鍵最大能儲存512MB。

⑵ Hash

  • Redis hash 是一個鍵值對集合。
  • Redis hash 是一個 string 型別的 field 和 value 的對映表,hash 特別適合用於儲存物件。

例項:

1234567 redis127.0.0.1:6379>HMSET user:1username zfpx password123OKredis127.0.0.1:6379>HGETALL user:11)"username"2)"zfpx"3)"password"4)"123"

以上例項中 hash 資料型別儲存了包含使用者指令碼資訊的使用者物件。 例項中我們使用了 Redis HMSET, HGETALL 命令,user:1 為鍵值。 每個 hash 可以儲存 232 – 1 鍵值對(40多億)

⑶ List

Redis 列表是簡單的字串列表,按照插入順序排序。你可以新增一個元素導列表的頭部(左邊)或者尾部(右邊)

例項:

12345678910 redis127.0.0.1:6379>lpush name zfpx1(integer)1redis127.0.0.1:6379>lpush name zfpx2(integer)2redis127.0.0.1:6379>lpush name zfpx3(integer)3redis127.0.0.1:6379>lrange name0-11)"zfpx3"2)"zfpx2"3)"zfpx1"

列表最多可儲存 232 – 1 元素 (4294967295, 每個列表可儲存40多億)。

⑷ Sets

Redis的Set是string型別的無序集合。 集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1)。

新增一個string元素到 key 對應的 set 集合中,成功返回1,如果元素已經在集合中返回0,key對應的set不存在返回錯誤,指令格式為

1 sadd key member

例項:

123456789101112 redis127.0.0.1:6379>sadd school zfpx1(integer)1redis127.0.0.1:6379>sadd school zfpx1(integer)0redis127.0.0.1:6379>sadd school zfpx2(integer)1redis127.0.0.1:6379>sadd school zfpx2(integer)0redis127.0.0.1:6379>smembers school1)"zfpx1"2)"zfpx2"

注意:以上例項中 zfpx1 添加了兩次,但根據集合內元素的唯一性,第二次插入的元素將被忽略。 集合中最大的成員數為 232 – 1 (4294967295, 每個集合可儲存40多億個成員)。

⑸ sorted sets/zset

Redis zset 和 set 一樣也是string型別元素的集合,且不允許重複的成員。 不同的是每個元素都會關聯一個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。

zset的成員是唯一的,但分數(score)卻可以重複。可以通過 zadd 命令(格式如下) 新增元素到集合,若元素在集合中存在則更新對應score

1 zadd key score member

例項:

1234567891011121314 redis127.0.0.1:6379>zadd school0zfpx1(integer)1redis127.0.0.1:6379>zadd school2zfpx2(integer)1redis127.0.0.1:6379>zadd school0zfpx3(integer)1redis127.0.0.1:6379>zadd school1zfpx4(integer)0redis127.0.0.1:6379>ZRANGEBYSCORE school01001)"zfpx1"2)"zfpx3"3)"zfpx4"4)"zfpx2"

1.2 效能

Redis資料庫完全在記憶體中,因此處理速度非常快,每秒能執行約11萬集合,每秒約81000+條記錄(測試資料的可參考這篇《

Redis的資料能確保一致性——所有Redis操作是原子性(Atomicity,意味著操作的不可再分,要麼執行要麼不執行)的,這保證瞭如果兩個客戶端同時訪問的Redis伺服器將獲得更新後的值。

1.3 持久化

通過定時快照(snapshot)和基於語句的追加(AppendOnlyFile,aof)兩種方式,redis可以支援資料持久化——將記憶體中的資料儲存到磁碟上,方便在宕機等突發情況下快速恢復。

1.4 CAP類別

屬於CP型別瞭解更多

2. Node下的使用

node 下可使用 node_redis 來實現 redis 客戶端操作:

1234567891011121314151617181920 varredis=require("redis"),client=redis.createClient();// if you'd like to select database 3, instead of 0 (default), call// client.select(3, function() { /* ... */ });client.on("error",function(err){console.log("Error "+err);});client.set("string key","string val",redis.print);client.hset("hash key","hashtest 1","some value",redis.print);client.hset(["hash key","hashtest 2","some other value"],redis.print);client.hkeys("hash key",function(err,replies){console.log(replies.length+" replies:");replies.forEach(function(reply,i){console.log("    "+i+": "+reply);});client.quit();});

3. 優缺點

優勢

1. 非常豐富的資料結構;

2. Redis提供了事務的功能,可以保證一串 命令的原子性,中間不會被任何操作打斷;

3. 資料存在記憶體中,讀寫非常的高速,可以達到10w/s的頻率。

缺點

1. Redis3.0後才出來官方的叢集方案,但仍存在一些架構上的問題出處

2. 持久化功能體驗不佳——通過快照方法實現的話,需要每隔一段時間將整個資料庫的資料寫到磁碟上,代價非常高;而aof方法只追蹤變化的資料,類似於mysql的binlog方法,但追加log可能過大,同時所有操作均要重新執行一遍,恢復速度慢;

3. 由於是記憶體資料庫,所以,單臺機器,儲存的資料量,跟機器本身的記憶體大小。雖然redis本身有key過期策略,但是還是需要提前預估和節約記憶體。如果記憶體增長過快,需要定期刪除資料。

適用場景

適用於資料變化快且資料庫大小可遇見(適合記憶體容量)的應用程式。更具體的可參照這篇《Redis 的 5 個常見使用場景》譯文。

MongoDB

MongoDB 是一個高效能,開源,無模式的文件型資料庫,開發語言是C++。它在許多場景下可用於替代傳統的關係型資料庫或鍵/值儲存方式。

1.特點

1.1 資料格式

在 MongoDB 中,文件是對資料的抽象,它的表現形式就是我們常說的 BSON(Binary JSON )

BSON 是一個輕量級的二進位制資料格式。MongoDB 能夠使用 BSON,並將 BSON 作為資料的儲存存放在磁碟中。

BSON 是為效率而設計的,它只需要使用很少的空間,同時其編碼和解碼都是非常快速的。即使在最壞的情況下,BSON格式也比JSON格式再最好的情況下儲存效率高。

對於前端開發者來說,一個“文件”就相當於一個物件: