幾款主流 NoSql 數據庫的對比

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 可以看爲這樣的關係:

複製代碼
columId1 = { //id=1的行
    article: {  //ColumnFamily-article
        title: XXX,  //ColumnFamily-article下的key之一
        content: XXX,
        tags: XXX
    },
    author: {  //ColumnFamily-author
        name: XXX
        nickname: 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位系統可能無法直接通過安裝包安裝。

官方示例:

複製代碼
var assert = require('assert');
var hbase = 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);
      });
    });
  });
});
複製代碼

數據檢索:

複製代碼
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。

實例:

redis 127.0.0.1:6379> SET name zfpx
OK
redis 127.0.0.1:6379> GET name
"zfpx"

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

⑵ Hash

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

實例:

複製代碼
redis 127.0.0.1:6379> HMSET user:1 username zfpx password 123
OK
redis 127.0.0.1:6379> HGETALL user:1
1) "username"
2) "zfpx"
3) "password"
4) "123"
複製代碼

以上實例中 hash 數據類型存儲了包含用戶腳本信息的用戶對象。 實例中我們使用了 Redis HMSET, HGETALL 命令,user:1 爲鍵值。 每個 hash 可以存儲 232 - 1 鍵值對(40多億)

⑶ List

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

實例:

複製代碼
redis 127.0.0.1:6379> lpush name zfpx1
(integer) 1
redis 127.0.0.1:6379> lpush name zfpx2
(integer) 2
redis 127.0.0.1:6379> lpush name zfpx3
(integer) 3
redis 127.0.0.1:6379> lrange name 0 -1
1) "zfpx3"
2) "zfpx2"
3) "zfpx1"
複製代碼

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

⑷ Sets

Redis的Set是string類型的無序集合。 集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是O(1)。

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

sadd key member

實例:

複製代碼
redis 127.0.0.1:6379> sadd school zfpx1
(integer) 1
redis 127.0.0.1:6379> sadd school zfpx1
(integer) 0
redis 127.0.0.1:6379> sadd school zfpx2
(integer) 1
redis 127.0.0.1:6379> sadd school zfpx2
(integer) 0
redis 127.0.0.1:6379> smembers school

1) "zfpx1"
2) "zfpx2"
複製代碼

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

⑸ sorted sets/zset

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

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

zadd key score member 

實例:

複製代碼
redis 127.0.0.1:6379> zadd school 0 zfpx1
(integer) 1
redis 127.0.0.1:6379> zadd school 2 zfpx2
(integer) 1
redis 127.0.0.1:6379> zadd school 0 zfpx3
(integer) 1
redis 127.0.0.1:6379> zadd school 1 zfpx4
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE school 0 100

1) "zfpx1"
2) "zfpx3"
3) "zfpx4"
4) "zfpx2"
複製代碼

1.2 性能

Redis數據庫完全在內存中,因此處理速度非常快,每秒能執行約11萬集合,每秒約81000+條記錄(測試數據的可參考這篇《Redis千萬級的數據量的性能測試》)

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

 

1.3 持久化

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

 

1.4 CAP類別

屬於CP類型瞭解更多

2. Node下的使用

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

複製代碼
var redis = 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格式再最好的情況下存儲效率高。

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

{“name":"mengxiangyue","sex":"nan"}  

對於文檔是有一些限制的:有序、區分大小寫的,所以下面的兩個文檔是與上面不同的:

{”sex“:"nan","name":"mengxiangyue"}  
{"Name":"mengxiangyue","sex":"nan"}  

另外,對於文檔的字段 MongoDB 有如下的限制:

_id必須存在,如果你插入的文檔中沒有該字段,那麼 MongoDB 會爲該文檔創建一個ObjectId作爲其值。_id的值必須在本集合中是唯一的。

多個文檔則組合爲一個“集合”。在 MongoDB 中的集合是無模式的,也就是說集合中存儲的文檔的結構可以是不同的,比如下面的兩個文檔可以同時存入到一個集合中:

{"name":"mengxiangyue"}  
{"Name":"mengxiangyue","sex":"nan"}  

1.2 性能

MongoDB 目前支持的存儲引擎爲內存映射引擎。當 MongoDB 啓動的時候,會將所有的數據文件映射到內存中,然後操作系統會託管所有的磁盤操作。這種存儲引擎有以下幾種特點:

* MongoDB 中關於內存管理的代碼非常精簡,畢竟相關的工作已經有操作系統進行託管。

* MongoDB 服務器使用的虛擬內存將非常巨大,並將超過整個數據文件的大小。不用擔心,操作系統會去處理這一切。

在《Mongodb億級數據量的性能測試》一文中,MongoDB 展現了強勁的大數據處理性能(數據甚至比Redis的漂亮的多)

另外,MongoDB 提供了全索引支持:包括文檔內嵌對象及數組。Mongo的查詢優化器會分析查詢表達式,並生成一個高效的查詢計劃。通常能夠極大的提高查詢的效率。

 

1.3 持久化

MongoDB 在1.8版本之後開始支持 journal,就是我們常說的 redo log,用於故障恢復和持久化。 

當系統啓動時,MongoDB 會將數據文件映射到一塊內存區域,稱之爲Shared view,在不開啓 journal 的系統中,數據直接寫入shared view,然後返回,系統每60s刷新這塊內存到磁盤,這樣,如果斷電或down機,就會丟失很多內存中未持久化的數據。

當系統開啓了 journal 功能,系統會再映射一塊內存區域供 journal 使用,稱之爲 private view,MongoDB 默認每100ms刷新 privateView 到 journal,也就是說,斷電或宕機,有可能丟失這100ms數據,一般都是可以忍受的,如果不能忍受,那就用程序寫log吧(但開啓journal後使用的虛擬內存是之前的兩倍)

 

1.4 CAP類別

MongoDB 比較靈活,可以設置成 strong consistent (CP類型)或者 eventual consistent(AP類型)

但其默認是 CP 類型瞭解更多

2. Node下的使用

MongoDB 在 node 環境下的驅動引擎是 node-mongodb-native ,作爲依賴封裝到 mongodb 包裏,我們直接安裝即可:

npm install mongodb

實例:

複製代碼
var mongodb = require('mongodb');

var mongodbServer = new mongodb.Server('localhost', 27017, { auto_reconnect: true, poolSize: 10 });
var db = new mongodb.Db('mydb', mongodbServer);

/* open db */
db.open(function() {
    /* Select 'contact' collection */
    db.collection('contact', function(err, collection) {
        /* Insert a data */
        collection.insert({
            name: 'Fred Chien',
            email: '[email protected]',
            tel: [
                '0926xxx5xx',
                '0912xx11xx'
            ]
        }, function(err, data) {
            if (data) {
                console.log('Successfully Insert');
            } else {
                console.log('Failed to Insert');
            }
        });

        /* Querying */
        collection.find({ name: 'Fred Chien' }, function(err, data) {
            /* Found this People */
            if (data) {
                console.log('Name: ' + data.name + ', email: ' + data.email);
            } else {
                console.log('Cannot found');
            }
        });
    });
});
複製代碼

另外我們也可以使用MongoDB的ODM(面向對象數據庫管理器) —— mongoose 來做數據庫管理,具體參照其API文檔。

3. 優缺點

優勢

1. 強大的自動化 shading 功能(更多戳這裏

2. 全索引支持,查詢非常高效;

3. 面向文檔(BSON)存儲,數據模式簡單而強大。

4. 支持動態查詢,查詢指令也使用JSON形式的標記,可輕易查詢文檔中內嵌的對象及數組。

5. 支持 javascript 表達式查詢,可在服務器端執行任意的 javascript函數。

 

缺點

1. 單個文檔大小限制爲16M,32位系統上,不支持大於2.5G的數據;

2. 對內存要求比較大,至少要保證熱數據(索引,數據及系統其它開銷)都能裝進內存;

3. 非事務機制,無法保證事件的原子性。


適用場景

1. 適用於實時的插入、更新與查詢的需求,並具備應用程序實時數據存儲所需的複製及高度伸縮性;

2. 非常適合文檔化格式的存儲及查詢;

3. 高伸縮性的場景:MongoDB 非常適合由數十或者數百臺服務器組成的數據庫。

4. 對性能的關注超過對功能的要求。

Couchbase 

本文之所以沒有介紹 CouchDB 或 Membase,是因爲它們合併了。合併之後的公司基於 Membase 與 CouchDB 開發了一款新產品,新產品的名字叫做 Couchbase。

Couchbase 可以說是集合衆家之長,目前應該是最先進的Cache系統,其開發語言是 C/C++。

Couchbase Server 是個面向文檔的數據庫(其所用的技術來自於Apache CouchDB項目),能夠實現水平伸縮,並且對於數據的讀寫來說都能提供低延遲的訪問(這要歸功於Membase技術)

1.特點

1.1 數據格式

Couchbase 跟 MongoDB 一樣都是面向文檔的數據庫,不過在往 Couchbase 插入數據前,需要先建立 bucket —— 可以把它理解爲“庫”或“表”。

因爲 Couchbase 數據基於 Bucket 而導致缺乏表結構的邏輯,故如果需要查詢數據,得先建立 view(跟RDBMS的視圖不同,view是將數據轉換爲特定格式結構的數據形式如JSON)來執行。

Bucket的意義 —— 在於將數據進行分隔,比如:任何 view 就是基於一個 Bucket 的,僅對 Bucket 內的數據進行處理。一個server上可以有多個Bucket,每個Bucket的存儲類型、內容佔用、數據複製數量等,都需要分別指定。從這個意義上看,每個Bucket都相當於一個獨立的實例。在集羣狀態下,我們需要對server進行集羣設置,Bucket只側重數據的保管。

每當views建立時, 就會建立indexes, index的更新和以往的數據庫索引更新區別很大。 比如現在有1W數據,更新了200條,索引只需要更新200條,而不需要更新所有數據,map/reduce功能基於index的懶更新行爲,大大得益。

要留意的是,對於所有文件,couchbase 都會建立一個額外的 56byte 的 metadata,這個 metadata 功能之一就是表明數據狀態,是否活動在內存中。同時文件的 key 也作爲標識符和 metadata 一起長期活動在內存中。

 

1.2 性能

couchbase 的精髓就在於依賴內存最大化降低硬盤I/O對吞吐量的負面影響,所以其讀寫速度非常快,可以達到亞毫秒級的響應。

couchbase在對數據進行增刪時會先體現在內存中,而不會立刻體現在硬盤上,從內存的修改到硬盤的修改這一步驟是由 couchbase 自動完成,等待執行的硬盤操作會以write queue的形式排隊等待執行,也正是通過這個方法,硬盤的I/O效率在 write queue 滿之前是不會影響 couchbase 的吞吐效率的。

鑑於內存資源肯定遠遠少於硬盤資源,所以如果數據量小,那麼全部數據都放在內存上自然是最優選擇,這時候couchbase的效率也是異常高。

但是數據量大的時候過多的數據就會被放在硬盤之中。當然,最終所有數據都會寫入硬盤,不過有些頻繁使用的數據提前放在內存中自然會提高效率。

 

1.3 持久化

其前身之一 memcached 是完全不支持持久化的,而 Couchbase 添加了對異步持久化的支持:

Couchbase提供兩種核心類型的buckets —— Couchbase 類型和 Memcached 類型。其中 Couchbase 類型提供了高可用和動態重配置的分佈式數據存儲,提供持久化存儲和複製服務。

Couchbase bucket 具有持久性 —— 數據單元異步從內存寫往磁盤,防範服務重啓或較小的故障發生時數據丟失。持久性屬性是在 bucket 級設置的。

 

1.4 CAP類型

Couchbase 羣集所有點都是對等的,只是在創建羣或者加入集羣時需要指定一個主節點,一旦結點成功加入集羣,所有的結點對等。

對等網的優點是,集羣中的任何節點失效,集羣對外提供服務完全不會中斷,只是集羣的容量受影響。

由於 couchbase 是對等網集羣,所有的節點都可以同時對客戶端提供服務,這就需要有方法把集羣的節點信息暴露給客戶端,couchbase 提供了一套機制,客戶端可以獲取所有節點的狀態以及節點的變動,由客戶端根據集羣的當前狀態計算 key 所在的位置。

就上述的介紹,Couchbase 明顯屬於 CP 類型。

2. Node下的使用

Couchbase 對 Node SDK 提供了官方文檔:http://docs.couchbase.com/couchbase-sdk-node-1.2/index.html

實例:

複製代碼
var couchbase = require("couchbase");

var bucket = new couchbase.Connection({
  'bucket':'beer-sample',
  'host':'127.0.0.1:8091'
}, function(err) {
  if (err) {
    // Failed to make a connection to the Couchbase cluster.
    throw err;
  }

  // 獲取文檔
  bucket.get('aass_brewery-juleol', function(err, result) {
    if (err) {
      // Failed to retrieve key
      throw err;
    }

    var doc = result.value;

    console.log(doc.name + ', ABV: ' + doc.abv);

    doc.comment = "Random beer from Norway";

    bucket.replace('aass_brewery-juleol', doc, function(err, result) {
      if (err) {
        // Failed to replace key
        throw err;
      }

      console.log(result);

      // Success!
      process.exit(0);
    });
  });
});
複製代碼

3. 優缺點

優勢

1. 高併發性,高靈活性,高拓展性,容錯性好;

2. 以 vBucket 的概念實現更理想化的自動分片以及動態擴容(瞭解更多);

 

缺點

1. Couchbase 的存儲方式爲 Key/Value,但 Value 的類型很爲單一,不支持數組。另外也不會自動創建doc id,需要爲每一文檔指定一個用於存儲的 Document Indentifer;

2. 各種組件拼接而成,都是c++實現,導致複雜度過高,遇到奇怪的性能問題排查比較困難(中文)文檔比較欠缺;

3. 採用緩存全部key的策略,需要大量內存。節點宕機時 failover 過程有不可用時間,並且有部分數據丟失的可能,在高負載系統上有假死現象;

4. 逐漸傾向於閉源,社區版本(免費,但不提供官方維護升級)和商業版本之間差距比較大。

 

適用場景

1. 適合對讀寫速度要求較高,但服務器負荷和內存花銷可遇見的需求;

2. 需要支持 memcached 協議的需求。

LevelDB 

LevelDB 是由谷歌重量級工程師(Jeff Dean 和 Sanjay Ghemawat)開發的開源項目,它是能處理十億級別規模 key-value 型數據持久性存儲的程序庫,開發語言是C++。

除了持久性存儲,LevelDB 還有一個特點是 —— 寫性能遠高於讀性能(當然讀性能也不差)

1.特點

LevelDB 作爲存儲系統,數據記錄的存儲介質包括內存以及磁盤文件,當LevelDB運行了一段時間,此時我們給LevelDb進行透視拍照,那麼您會看到如下一番景象:

(圖1)

LevelDB 所寫入的數據會先插入到內存的 Mem Table 中,再由 Mem Table 合併到只讀且鍵值有序的 Disk TableSSTable 中,再由後臺線程不時的對 Disk Table 進行歸併。

內存中存在兩個 Mem Table —— 一個是可以往裏面寫數據的table A,另一個是正在合併到硬盤的 table B。

Mem Table 用 skiplist 實現,寫數據時,先寫日誌(.log),再往A插入,因爲一次寫入操作只涉及一次磁盤順序寫和一次內存寫入,所以這是爲何說LevelDb寫入速度極快的主要原因。如果當B還沒完成合並,而A已經寫滿時,寫操作必須等待。

 

DiskTable(SSTable,格式爲.sst)是分層的(leveldb的名稱起源),每一個大小不超過2M。最先 dump 到硬盤的 SSTable 的層級爲0,層級爲0的 SSTable 的鍵值範圍可能有重疊。如果這樣的 SSTable 太多,那麼每次都需要從多個 SSTable 讀取數據,所以LevelDB 會在適當的時候對 SSTable 進行 Compaction,使得新生成的 SSTable 的鍵值範圍互不重疊。

進行對層級爲 level 的 SSTable 做 Compaction 的時候,取出層級爲 level+1 的且鍵值空間與之重疊的 Table,以順序掃描的方式進行合併。level 爲0的 SSTable 做 Compaction 有些特殊:會取出 level 0 所有重疊的Table與下一層做 Compaction,這樣做保證了對於大於0的層級,每一層裏 SSTable 的鍵值空間是互不重疊的。

SSTable 中的某個文件屬於特定層級,而且其存儲的記錄是 key 有序的,那麼必然有文件中的最小 key 和最大 key,這是非常重要的信息,LevelDB 應該記下這些信息 —— Manifest 就是幹這個的,它記載了 SSTable 各個文件的管理信息,比如屬於哪個Level,文件名稱叫啥,最小 key 和最大 key 各自是多少。下圖是 Manifest 所存儲內容的示意:

圖中只顯示了兩個文件(Manifest 會記載所有 SSTable 文件的這些信息),即 Level0 的 Test1.sst 和 Test2.sst 文件,同時記載了這些文件各自對應的 key 範圍,比如 Test1.sstt 的 key 範圍是“an”到 “banana”,而文件 Test2.sst 的 key 範圍是“baby”到“samecity”,可以看出兩者的 key 範圍是有重疊的。

那麼上方圖1中的 Current 文件是幹什麼的呢?這個文件的內容只有一個信息,就是記載當前的 Manifest 文件名。因爲在 LevleDB 的運行過程中,隨着 Compaction 的進行,SSTable 文件會發生變化,會有新的文件產生,老的文件被廢棄,Manifest 也會跟着反映這種變化,此時往往會新生成 Manifest 文件來記載這種變化,而 Current 則用來指出哪個 Manifest 文件纔是我們關心的那個 Manifest 文件。

注意,鑑於 LevelDB 不屬於分佈式數據庫,故CAP法則在此處不適用。

2. Node下的使用

Node 下可以使用 LevelUP 來操作 LevelDB 數據庫:

複製代碼
var levelup = require('levelup')

// 1) Create our database, supply location and options.
//    This will create or open the underlying LevelDB store.
var db = levelup('./mydb')

// 2) put a key & value
db.put('name', 'LevelUP', function (err) {
  if (err) return console.log('Ooops!', err) // some kind of I/O error

  // 3) fetch by key
  db.get('name', function (err, value) {
    if (err) return console.log('Ooops!', err) // likely the key was not found

    // ta da!
    console.log('name=' + value)
  })
})
複製代碼

LevelUp 的API非常簡潔實用,具體可參考官方文檔。

3. 優缺點

優勢

1. 操作接口簡單,基本操作包括寫記錄,讀記錄和刪除記錄,也支持針對多條操作的原子批量操作;

2. 寫入性能遠強於讀取性能,

3. 數據量增大後,讀寫性能下降趨平緩。

 

缺點

1. 隨機讀性能一般;

2. 對分佈式事務的支持還不成熟。而且機器資源浪費率高。

 

適應場景

適用於對寫入需求遠大於讀取需求的場景(大部分場景其實都是這樣)

References

hbase快速入門 —— http://wangmored.iteye.com/blog/1727731

8種 NoSQL 數據庫系統對比 —— http://blog.jobbole.com/1344/

node-hbase —— https://github.com/wdavidw/node-hbase

HBase 超詳細介紹 —— http://blog.csdn.net/frankiewang008/article/details/41965543

HBase 將主導 NoSQL 嗎 —— http://www.oschina.net/translate/big-data-debate-will-hbase-dominate-nosq

memcached,redis,mongodb的區別以及優缺點 —— http://blog.csdn.net/senssic/article/details/30511543

redis的優點和缺點 —— http://joezhengjinhong.blog.51cto.com/7791846/1565754

redis入門 (一)認識redis(該文章有部分示例不正確,引用時做了修正) —— http://www.zhufengpeixun.cn/jishuziliao/Nodejishuziliao/2015-11-23/410.html

MongoDB文檔、集合、數據庫概念 —— http://blog.csdn.net/mengxiangyue/article/details/9879925

mongodb 持久化(5) —— http://ju.outofmemory.cn/entry/81554

關於Mongodb的全面總結 —— http://blog.csdn.net/shellching/article/details/7651979

Couchbase的簡單介紹 —— http://bbs.byr.cn/#!article/Database/8365

Memcache升級版:CouchBase(一)安裝篇 —— http://blog.hackroad.com/operations-engineer/linux_server/8380.html

Couchbase介紹,更好的Cache系統 —— http://zhang.hu/couchbase/

Couchbase第一印象(架構特性) —— http://www.bubuko.com/infodetail-550423.html

NoSQL選型詳解 —— http://www.thebigdata.cn/JieJueFangAn/6476.html

數據分析與處理之二(Leveldb 實現原理)—— http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html

消息中間件剖析 —— http://blog.lday.me/?p=170

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章