日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

LevelDB:一個(gè)快速輕量級(jí)的key-value存儲(chǔ)庫(kù)

系統(tǒng) 2069 0

???? 作者: Jeff?Dean,?Sanjay?Ghemawat

原文: http://leveldb.googlecode.com/svn/trunk/doc/index.html

譯者: phylips@bmy?2011-8-16

譯文: http://duanple.blog.163.com/blog/static/70971767201171705113636/

LevelDB 庫(kù)提供了一種永久性的 key?value 存儲(chǔ)。 Key value 都是任意的字節(jié)序列。在這個(gè) key?value 存儲(chǔ)系統(tǒng)中, key 按照用戶(hù)聲明的比較函數(shù)有序排列。

打開(kāi)一個(gè)數(shù)據(jù)庫(kù)

一個(gè) LevelDB 數(shù)據(jù)庫(kù)有一個(gè)文件系統(tǒng)目錄名稱(chēng)與之關(guān)聯(lián)。該數(shù)據(jù)庫(kù)的所有內(nèi)容都存儲(chǔ)在該目錄下。下面的例子展示了如何打開(kāi)一個(gè)數(shù)據(jù)庫(kù),或者如何在必要的時(shí)候創(chuàng)建一個(gè):

#include?<assert>

#include?"leveldb/db.h"

leveldb::DB*?db;

leveldb::Options?options;

options.create_if_missing?=?true;

leveldb::Status?status?=?leveldb::DB::Open(options,?"/tmp/testdb",?&db);

assert(status.ok());

...

如果你想在數(shù)據(jù)庫(kù)已經(jīng)存在的情況下,讓上面的代碼產(chǎn)生一個(gè)錯(cuò)誤,需要再 Open 調(diào)用之前加入如下一行:

options.error_if_exists?=?true;

狀態(tài) (status)

你可能注意到了上面的 leveldb::Status 數(shù)據(jù)類(lèi)型。在 LevelDB 中絕大多數(shù)可能出錯(cuò)的函數(shù)調(diào)用都會(huì)返回這種類(lèi)型的值。也可以在出錯(cuò)的情況下,打印出一些錯(cuò)誤提示信息

???leveldb::Status?s?=?...;

???if?(!s.ok())?cerr?<<?s.ToString()?<<?endl;

關(guān)閉數(shù)據(jù)庫(kù)

在完成一個(gè)數(shù)據(jù)庫(kù)的處理之后,直接刪除該數(shù)據(jù)庫(kù)對(duì)象即可。

...?open?the?db?as?described?above?...

??...?do?something?with?db?...

??delete?db;

讀操作與寫(xiě)操作

數(shù)據(jù)庫(kù)提供了 Put,?Delete,? 和? Get 方法來(lái)對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改 / 查詢(xún)。比如下面的代碼會(huì)將存儲(chǔ)在 key1 下面的值存到 key2 下面。

??std::string?value;

??leveldb::Status?s?=?db->Get(leveldb::ReadOptions(),?key1,?&value);

??if?(s.ok())?s?=?db->Put(leveldb::WriteOptions(),?key2,?value);

??if?(s.ok())?s?=?db->Delete(leveldb::WriteOptions(),?key1);

原子性更新

需要注意的是。在上面的操作中,如果在 Put?key2 之后, delete?key1 之前,進(jìn)程死掉了,那么相同的 value 值就可能會(huì)存儲(chǔ)在多個(gè) key 值下面。可以通過(guò)使用 WriteBatch 類(lèi)原子性的執(zhí)行一組更新操作,來(lái)避免這樣的問(wèn)題:

??#include?"leveldb/write_batch.h"

??...

??std::string?value;

??leveldb::Status?s?=?db->Get(leveldb::ReadOptions(),?key1,?&value);

??if?(s.ok())?{

????leveldb::WriteBatch?batch;

????batch.Delete(key1);

????batch.Put(key2,?value);

????s?=?db->Write(leveldb::WriteOptions(),?&batch);

??}

WriteBatch

會(huì)持有對(duì)數(shù)據(jù)庫(kù)的一系列更改操作,這些操作會(huì)按照它們加入順序被執(zhí)行。除了提供這種原子性的保證外, WriteBatch 還可以通過(guò)將多個(gè)更新放到同一個(gè) batch 里,在存在大量更新操作時(shí),加速它們的執(zhí)行。

同步性的寫(xiě)操作

默認(rèn)情況下,對(duì)于 leveldb 的寫(xiě)操作是異步的:在將寫(xiě)操作從進(jìn)程交給操作系統(tǒng)之后它就會(huì)返回。從操作系統(tǒng)內(nèi)存空間到底層永久性存儲(chǔ)設(shè)備之間的傳輸是異步進(jìn)行的。可以通過(guò)打開(kāi) sync?flag 來(lái),使得一個(gè)寫(xiě)操作要等到數(shù)據(jù)真正的寫(xiě)入到永久性存儲(chǔ)后才返回。 ( Posix 系統(tǒng)中,這是通過(guò)在寫(xiě)操作返回之前調(diào)用 fsync(...)? 或者? fdatasync(...)? 或者是? msync(...,?MS_SYNC)) 來(lái)實(shí)現(xiàn)的 )

??leveldb::WriteOptions?write_options;

??write_options.sync?=?true;

??db->Put(write_options,?...);

異步性的寫(xiě)操作通常要比同步性的快 1000 倍。它的缺點(diǎn)是,當(dāng)機(jī)器 crash 掉的時(shí)候,可能會(huì)丟失最后的一些更新。需要注意的是,如果僅僅是寫(xiě)操作進(jìn)程的 crash( 比如,不是 reboot) 并不會(huì)引起任何的丟失,因?yàn)榧词? sync=false ,一個(gè)更新操作在完成之前必須要把數(shù)據(jù)從應(yīng)用程序空間傳到系統(tǒng)空間。

異步性的寫(xiě)操作通常都可以安全的使用。比如,在將大量數(shù)據(jù)加載到數(shù)據(jù)庫(kù)中時(shí),你可以通過(guò)在 crash 之后重新加載一遍來(lái)處理那些丟失的更新。也可以采用一種混合模式,比如每隔 N 個(gè)寫(xiě)操作,進(jìn)行一次同步,這樣在 crash 發(fā)生時(shí),只需要從最后一次同步的地方開(kāi)始重新執(zhí)行即可 ( 只需要讓同步性的寫(xiě)操作更新一個(gè)重啟時(shí)從何處開(kāi)始的標(biāo)記即可 )

WriteBatch 也可以為異步性的寫(xiě)操作的提供一個(gè)改進(jìn)。多個(gè)更新操作可以放到同一個(gè) WriteBatch ,然后使用一個(gè)同步性的寫(xiě)操作 ( 比如令 write_options.sync=true) 。這樣額外的同步開(kāi)銷(xiāo)就可以分?jǐn)偟蕉鄠€(gè)寫(xiě)操作中。

并發(fā)

同一時(shí)刻只能有一個(gè)進(jìn)程打開(kāi)數(shù)據(jù)庫(kù)。為了防止誤用, LevelDB 實(shí)現(xiàn)會(huì)從操作系統(tǒng)申請(qǐng)一把鎖。在一個(gè)進(jìn)程內(nèi)部,同一個(gè) leveldb::DB 對(duì)象可以安全地被多個(gè)并發(fā)線(xiàn)程共享。比如,多個(gè)線(xiàn)程可以在同一個(gè)數(shù)據(jù)庫(kù)中寫(xiě)數(shù)據(jù),獲取迭代器,執(zhí)行 Get 調(diào)用而不需要額外的同步 (LevelDB 實(shí)現(xiàn)會(huì)自動(dòng)的完成所需的同步 ) 。然而其他對(duì)象 ( 比如迭代器和 WriteBatch) 可能需要額外的同步。如果兩個(gè)線(xiàn)程共享相同的此類(lèi)對(duì)象,它們必須使用自己的鎖機(jī)制來(lái)保護(hù)對(duì)此類(lèi)對(duì)象的訪(fǎng)問(wèn)。

迭代

下面的例子用來(lái)說(shuō)明如何打印出數(shù)據(jù)庫(kù)中的所有 key?value 對(duì)。

??leveldb::Iterator*?it?=?db->NewIterator(leveldb::ReadOptions());

??for?(it->SeekToFirst();?it->Valid();?it->Next())?{

????cout?<<?it->key().ToString()?<<?":?"??<<?it->value().ToString()?<<?endl;

??}

??assert(it->status().ok());?

//?Check?for?any?errors?found?during?the?scan

??delete?it;

下面的例子說(shuō)明如何只處理那些給定 key 值邊界 [start,limit) 內(nèi)的數(shù)據(jù) :

??for?(it->Seek(start);

???????it->Valid()?&&?it->key().ToString()?<?limit;

???????it->Next())?{

????...

??}

也可以以逆序方式進(jìn)行處理? ( 可能比順序處理慢些 )

??for?(it->SeekToLast();?it->Valid();?it->Prev())?{

????...

??}

Snapshots

Snapshots 提供了關(guān)于整個(gè) key-value 存儲(chǔ)狀態(tài)的一致性的只讀視圖。可以通過(guò)設(shè)置 ReadOptions::snapshot 為非空值,來(lái)指定從數(shù)據(jù)庫(kù)的某個(gè)特殊的版本中讀取。如果它的值為空,則默認(rèn)讀取當(dāng)前狀態(tài)。 Snapshots 通常通過(guò) DB::GetSnapshot() 方法創(chuàng)建:

??leveldb::ReadOptions?options;

??options.snapshot?=?db->GetSnapshot();

??...?apply?some?updates?to?db?...

??leveldb::Iterator*?iter?=?db->NewIterator(options);

??...?read?using?iter?to?view?the?state?when?the?snapshot?was?created?...

??delete?iter;

??db->ReleaseSnapshot(options.snapshot);

需要注意,在一個(gè) snapshot 不再需要的時(shí)候,必須通過(guò) DB::ReleaseSnapshot 接口來(lái)顯示的進(jìn)行釋放 。這樣就可以讓底層實(shí)現(xiàn)丟棄掉那些為支持對(duì)該 snapshot 的讀取操作所維護(hù)的一些狀態(tài)數(shù)據(jù)。一個(gè)寫(xiě)操作也可以返回一個(gè)應(yīng)用了一系列特殊的更新操作集合后的數(shù)據(jù)庫(kù)狀態(tài)的 snapshot:

??leveldb::Snapshot*?snapshot;

??leveldb::WriteOptions?write_options;

??write_options.post_write_snapshot?=?&snapshot;

??leveldb::Status?status?=?db->Write(write_options,?...);

??...?perform?other?mutations?to?db?...

??leveldb::ReadOptions?read_options;

??read_options.snapshot?=?snapshot;

??leveldb::Iterator*?iter?=?db->NewIterator(read_options);

??...?read?as?of?the?state?just?after?the?Write?call?returned?...

??delete?iter;

??db->ReleaseSnapshot(snapshot);

Slice

上面 it->key() 和? it->value() 調(diào)用的返回值都是 leveldb::Slice 類(lèi)型。 Slice 是一個(gè)包含了長(zhǎng)度及指向外部字節(jié)數(shù)組的指針的簡(jiǎn)單結(jié)構(gòu)。返回 Slice 比返回一個(gè) std::string 類(lèi)型開(kāi)銷(xiāo)要低,因?yàn)檫@種我們就不必對(duì)那些大的 key value 值進(jìn)行拷貝。另外, LevelDB 方法也不能返回以 null 結(jié)尾的 C 風(fēng)格字符串,因?yàn)樗? key value 都允許包含 '\0'

C++?string 和以 null 結(jié)尾的 C 風(fēng)格字符串可以簡(jiǎn)單的轉(zhuǎn)化為一個(gè) Slice 類(lèi)型:

???leveldb::Slice?s1?=?"hello";

???std::string?str("world");

???leveldb::Slice?s2?=?str;

一個(gè) Slice 類(lèi)型也可以簡(jiǎn)單的轉(zhuǎn)化為一個(gè) C++?string 類(lèi)型:

???std::string?str?=?s1.ToString();

???assert(str?==?std::string("hello"));

在使用 Slices 需要格外小心,因?yàn)樗蕾?lài)于調(diào)用者去保證 Slice 所指向的外部字節(jié)數(shù)組的有效性 。比如下面代碼的就是有問(wèn)題的:

???leveldb::Slice?slice;

???if?(...)?{

?????std::string?str?=?...;

?????slice?=?str;

???}

???Use(slice);

因?yàn)? if 語(yǔ)句的作用域已經(jīng)結(jié)束, str 將會(huì)被析構(gòu),這樣 slice 指向的空間就不存在了。

比較器

前面的那些例子為 key 使用了默認(rèn)的排序函數(shù),會(huì)按字典序進(jìn)行排序。你可以在打開(kāi)數(shù)據(jù)庫(kù)的時(shí)候提供一個(gè)自定義的比較器。比如假設(shè)數(shù)據(jù)庫(kù)的 key 是由兩個(gè)數(shù)組成,我們首先按照第一個(gè)數(shù)進(jìn)行排序,如果相等再比較第二個(gè)。首先需要定義一個(gè)滿(mǎn)足如下規(guī)則的 leveldb::Comparator 的子類(lèi):

??class?TwoPartComparator?:?public?leveldb::Comparator?{

???public:

????//?Three-way?comparison?function:

????//???if?a?<?b:?negative?result

????//???if?a?>?b:?positive?result

????//???else:?zero?result

????int?Compare(const?leveldb::Slice&?a,?const?leveldb::Slice&?b)?const?{

??????int?a1,?a2,?b1,?b2;

??????ParseKey(a,?&a1,?&a2);

??????ParseKey(b,?&b1,?&b2);

??????if?(a1?<?b1)?return?-1;

??????if?(a1?>?b1)?return?+1;

??????if?(a2?<?b2)?return?-1;

??????if?(a2?>?b2)?return?+1;

??????return?0;

????}

????//?Ignore?the?following?methods?for?now:

????const?char*?Name()?const?{?return?"TwoPartComparator";?}

????void?FindShortestSeparator(std::string*,?const?leveldb::Slice&)?const?{?}

????void?FindShortSuccessor(std::string*)?const?{?}

??};

現(xiàn)在使用定制的比較器,創(chuàng)建一個(gè)數(shù)據(jù)庫(kù) :

??TwoPartComparator?cmp;

??leveldb::DB*?db;

??leveldb::Options?options;

??options.create_if_missing?=?true;

??options.comparator?=?&cmp;

??leveldb::Status?status?=?leveldb::DB::Open(options,?"/tmp/testdb",?&db);

向后兼容 (Backwards?compatibility)

比較器的 Name 方法的返回結(jié)果會(huì)在數(shù)據(jù)庫(kù)創(chuàng)建時(shí)與之綁定,同時(shí)在后面每次打開(kāi)數(shù)據(jù)庫(kù)時(shí)都會(huì)進(jìn)行檢查。如果返回名稱(chēng)發(fā)生了變化,那么 leveldb::DB::Open 調(diào)用會(huì)失敗。因此,當(dāng)且僅當(dāng)新的 key 格式及比較函數(shù)與現(xiàn)有數(shù)據(jù)庫(kù)不兼容的時(shí)候才需要去改變它,同時(shí)此時(shí)丟棄掉所有現(xiàn)存數(shù)據(jù)庫(kù)的內(nèi)容也應(yīng)是可以接受的。

你也可以有計(jì)劃的逐步改變 key 的格式。比如,可以在每個(gè) key 的后面存儲(chǔ)一個(gè)版本號(hào) ( 對(duì)于大多數(shù)的情況一個(gè)字節(jié)就足夠了 ) 。當(dāng)切換到一種新的 key 格式 ( 比如,為 TwoPartComparator 處理的 key 增加一個(gè)可選的第三塊內(nèi)容 ) (a) 保持相同的比較器名稱(chēng) (b) 新的 key 增加版本號(hào) (c) 改變比較器函數(shù),使得它可以通過(guò) key 里面的版本號(hào)來(lái)決定如何解釋它們。

性能

可以通過(guò)改變 include/leveldb/options.h 里的默認(rèn)值來(lái)對(duì)性能進(jìn)行調(diào)整優(yōu)化。

塊大小

LevelDB 會(huì)將相鄰的 key 值組合在一塊放到同一個(gè) block 中,這樣的一個(gè) block 是與永久性存儲(chǔ)設(shè)備進(jìn)行傳輸?shù)幕締卧DJ(rèn)的塊大小大概是 4096 個(gè)未壓縮字節(jié)。那些經(jīng)常需要掃描整個(gè)數(shù)據(jù)庫(kù)內(nèi)容的應(yīng)用可能希望增大這個(gè)值。那些讀取大量小的 value 值的應(yīng)用,如果可以得到性能上的改進(jìn),可能傾向于將這個(gè)值設(shè)得更小。如果這個(gè)值過(guò)大或過(guò)小,也不會(huì)有太多好處,比如小于 1KB 或者大于數(shù) MB 的情況下。需要指出的是,對(duì)于那些大的 block 大小,壓縮可能會(huì)更有效。

壓縮

每個(gè)塊被寫(xiě)入永久性存儲(chǔ)設(shè)備之前會(huì)被單獨(dú)壓縮。由于默認(rèn)的壓縮方法速度很快,同時(shí)對(duì)于那些很難壓縮的數(shù)據(jù)它會(huì)自動(dòng)的關(guān)閉,因此默認(rèn)情況下壓縮就是開(kāi)啟的。只有在很特殊的情況下,應(yīng)用才會(huì)去關(guān)閉壓縮,但是只有當(dāng)通過(guò) benchmarks 能看到性能提升時(shí)才應(yīng)該這樣做。

??leveldb::Options?options;

??options.compression?=?leveldb::kNoCompression;

??...?leveldb::DB::Open(options,?name,?...)?....

緩存

數(shù)據(jù)庫(kù)內(nèi)容被存儲(chǔ)在文件系統(tǒng)的一系列文件中,每個(gè)文件保存了一系列的壓縮的 blocks 。如果 options.cache 值是非 NULL 的,這時(shí)那些已經(jīng)用過(guò)的未壓縮的塊內(nèi)容會(huì)被緩存。

??#include?"leveldb/cache.h"

??leveldb::Options?options;

??options.cache?=?leveldb::NewLRUCache(100?*?1048576);??//?100MB?cache

??leveldb::DB*?db;

??leveldb::DB::Open(options,?name,?&db);

??...?use?the?db?...

??delete?db

??delete?options.cache;

需要說(shuō)明的是,緩存里存放的是未壓縮數(shù)據(jù),因此它的大小是應(yīng)用級(jí)別的數(shù)據(jù)大小,而不是壓縮后的 ( 對(duì)于已壓縮的 blocks 的緩存交給操作系統(tǒng) buffer 去緩存,或者是由客戶(hù)端提供自己的 Env 實(shí)現(xiàn)來(lái)完成 ) 在執(zhí)行大的讀操作的時(shí)候,應(yīng)用程序可能希望不使用緩存,這樣就可以避免這些大數(shù)據(jù)的讀操作消耗掉大量的緩存 。一個(gè)針對(duì)迭代器的 option 參數(shù)可以實(shí)現(xiàn)這個(gè)目的:

??leveldb::ReadOptions?options;

??options.fill_cache?=?false;

??leveldb::Iterator*?it?=?db->NewIterator(options);

??for?(it->SeekToFirst();?it->Valid();?it->Next())?{

????...

??}

Key layout

需要注意的是磁盤(pán)傳輸和緩存的單位是 block 。相鄰的 key( 根據(jù)數(shù)據(jù)庫(kù)排序結(jié)果 ) 通常會(huì)被放到同一個(gè) block 里。因此應(yīng)用程序可以通過(guò)將那些相鄰近的 key 值放到一塊進(jìn)行訪(fǎng)問(wèn),將那些很少被使用的 key 值放到一個(gè)獨(dú)立的 key 值空間內(nèi)。

比如,假設(shè)在 LevelDB 之上實(shí)現(xiàn)一個(gè)簡(jiǎn)單的文件系統(tǒng)。通常需要存儲(chǔ)的記錄數(shù)據(jù)如下:

???filename?->?permission-bits,?length,?list?of?file_block_ids

???file_block_id?->?data

我們可以為 filename 添加個(gè)前綴 ( 比如 '/') ,為 file_block_id 加上另一個(gè) ( 比如 '0') ,這樣對(duì)于文件元數(shù)據(jù)的掃描就不不需要去獲取及緩存大量的文件內(nèi)容數(shù)據(jù)。

Checksums

LevelDB 會(huì)為它存儲(chǔ)在文件系統(tǒng)中的所有數(shù)據(jù)生成相應(yīng)的 checksums 。通常有兩種方式來(lái)控制對(duì) checksums 進(jìn)行何種程度的驗(yàn)證: ReadOptions::verify_checksums? 設(shè)為 true ,會(huì)強(qiáng)制對(duì)從文件系統(tǒng)中讀出的所有數(shù)據(jù)都進(jìn)行 checksum 驗(yàn)證。默認(rèn)情況下,不進(jìn)行驗(yàn)證。 Options::paranoid_checks? 可以在打開(kāi)數(shù)據(jù)庫(kù)之前將其設(shè)為 true ,這會(huì)使得?數(shù)據(jù)庫(kù)底層實(shí)現(xiàn)只要檢測(cè)到內(nèi)部數(shù)據(jù)錯(cuò)誤時(shí)就會(huì)產(chǎn)生一個(gè) error Error 產(chǎn)生的時(shí)機(jī)取決于數(shù)據(jù)庫(kù)的哪個(gè)部分出了問(wèn)題,比如可能在數(shù)據(jù)庫(kù)打開(kāi)時(shí)或者是在后面執(zhí)行某個(gè)數(shù)據(jù)庫(kù)操作時(shí)。默認(rèn)情況下, paranoid?checking 是關(guān)閉的,這樣即使數(shù)據(jù)庫(kù)的某些永久性存儲(chǔ)出了問(wèn)題,它仍然也是可以使用的。?如果數(shù)據(jù)庫(kù)被破壞了 ( 可能是因?yàn)榇蜷_(kāi)了 paranoid?checking 而導(dǎo)致它無(wú)法打開(kāi) ) leveldb::RepairDB 函數(shù)可以用來(lái)盡量地對(duì)數(shù)據(jù)進(jìn)行恢復(fù)。

Approximate?Sizes

GetApproximateSizes 方法可以被用來(lái)得到一個(gè)或多個(gè) key?range 所占用的文件系統(tǒng)空間的近似大小。

???leveldb::Range?ranges[2];

???ranges[0]?=?leveldb::Range("a",?"c");

???ranges[1]?=?leveldb::Range("x",?"z");

???uint64_t?sizes[2];

???leveldb::Status?s?=?db->GetApproximateSizes(ranges,?2,?sizes);

上面的調(diào)用,會(huì)設(shè)置 sizes[0] 的值為 range?[a..c) 所占用的文件系統(tǒng)空間的近似大小, sizes[1]? 的值為 range?[x..z) 所占用的文件系統(tǒng)空間的近似大小。

Environment

所有由 LevelDB 實(shí)現(xiàn)產(chǎn)生的文件操作 ( 及其他的操作系統(tǒng)調(diào)用 ) 都是通過(guò) leveldb::Env 對(duì)象統(tǒng)一管理。為了進(jìn)行更好的控制,某些客戶(hù)端可能希望提供自己的 Env 實(shí)現(xiàn)。比如應(yīng)用程序可能希望在文件 IO 中引入自定義的延時(shí)來(lái)限制 LevelDB 對(duì)系統(tǒng)其他動(dòng)作產(chǎn)生的影響。

??class?SlowEnv?:?public?leveldb::Env?{

????..?implementation?of?the?Env?interface?...

??};

??SlowEnv?env;

??leveldb::Options?options;

??options.env?=?&env;

??Status?s?=?leveldb::DB::Open(options,?...);

移植性 (Porting)

通過(guò)提供一個(gè) leveldb/port/port.h 中的 types/methods/functions 的平臺(tái)描述實(shí)現(xiàn),就可以將 LevelDB 移植到一個(gè)新平臺(tái)上。具體細(xì)節(jié)可以參考: leveldb/port/port_example.h

性能 (Performance)

下面是運(yùn)行 db_bench 程序得到的性能測(cè)試報(bào)告。結(jié)果有些雜亂,但是足以說(shuō)明問(wèn)題。

測(cè)試環(huán)境

使用一個(gè)具有百萬(wàn)條記錄的數(shù)據(jù)庫(kù),每條記錄有一個(gè) 16 字節(jié)的 key 100 字節(jié)的 value 。對(duì)于 values 值壓縮后可能大概只有原始大小的一半。

LevelDB:?version?1.1

Date:?Sun?May?1?12:11:26?2011

CPU:?4?x?Intel(R)?Core(TM)2?Quad?CPU?Q6600?@?2.40GHz

CPUCache:?4096?KB

Keys:?16?bytes?each

Values:?100?bytes?each?(50?bytes?after?compression)

Entries:?1000000

Raw?Size:?110.6?MB?(estimated)

File?Size:?62.9?MB?(estimated)

寫(xiě)性能

"fill"?benchmarks 會(huì)以順序地或者隨機(jī)的方式創(chuàng)建一個(gè)全新的數(shù)據(jù)庫(kù)。 "fillsync"?benchmark? 的每次操作都會(huì)將數(shù)據(jù)從操作系統(tǒng) flush 到磁盤(pán);其他的寫(xiě)操作會(huì)允許數(shù)據(jù)在操作系統(tǒng) buffer 中停留一段時(shí)間。 "overwrite"?benchmark? 會(huì)進(jìn)行隨機(jī)的寫(xiě)入以更新數(shù)據(jù)庫(kù)中現(xiàn)有的 key 值。

fillseq?:?1.765?micros/op;?62.7?MB/s

fillsync?:?268.409?micros/op;?0.4?MB/s?(10000?ops)

fillrandom?:?2.460?micros/op;?45.0?MB/s

overwrite?:?2.380?micros/op;?46.5?MB/s

上面的一次” op ”意味著對(duì)于單個(gè) key/value 對(duì)的一次寫(xiě)人。比如隨機(jī)寫(xiě) benchmark 每秒大概可以近似達(dá)到 400,000 寫(xiě)操作。

每個(gè) "fillsync" 操作花費(fèi) (0.3ms) 遠(yuǎn)小于一次磁盤(pán) seek 操作 ( 通常需要 10ms) 。我們懷疑這是因?yàn)橛脖P(pán)本身會(huì)將這些更新緩存到它的 memory 里,在數(shù)據(jù)真正寫(xiě)入到扇區(qū)之前就做出了響應(yīng)。這種情況下的安全性取決于硬盤(pán)在電力供應(yīng)出問(wèn)題時(shí)是否有足夠的電力去保存它的 memory 中的數(shù)據(jù)。

讀性能

我們列出了正向及反向順序讀的性能,以及隨機(jī)查找的性能。需要注意的是,由 benchmark 創(chuàng)建的數(shù)據(jù)庫(kù)是很小的。因此這個(gè)報(bào)告只是刻畫(huà)了工作集可以載入到內(nèi)存時(shí)的 LevelDB 的性能。對(duì)于那些未命中操作系統(tǒng)緩存的單片數(shù)據(jù)讀取操作來(lái)說(shuō),開(kāi)銷(xiāo)主要是由為從磁盤(pán)獲取數(shù)據(jù)所需進(jìn)行的一次或兩次磁盤(pán) seek 操作造成的。而寫(xiě)操作性能幾乎不受工作集能否載入到內(nèi)存的影響。

?readrandom?:?16.677?micros/op;?( 每秒大概 60,000?reads)

readseq?:?0.476?micros/op;?232.3?MB/s

readreverse?:?0.724?micros/op;?152.9?MB/s

LevelDB 為提高讀性能會(huì)在后臺(tái)壓縮它的底層存儲(chǔ)數(shù)據(jù)。上面的測(cè)試是在進(jìn)行過(guò)大量的隨機(jī)寫(xiě)操作之后立即進(jìn)行的。在進(jìn)行過(guò) compactions( 通常是自動(dòng)觸發(fā)的 ) 再進(jìn)行測(cè)試結(jié)果會(huì)更好些。

readrandom?:?11.602?micros/op;?( 每秒大概 85,000?reads)

readseq?:?0.423?micros/op;?261.8?MB/s

readreverse?:?0.663?micros/op;?166.9?MB/s

某些讀操作的高花費(fèi)是由于從硬盤(pán)讀取出的 blocks 的重復(fù)解壓導(dǎo)致的。如果我們可以為 LevelDB 提供足夠的緩存使得它可以將所有的未壓縮塊放入內(nèi)存,那么讀性能會(huì)有更大地改善:

readrandom?:?9.775?micros/op;?( 每秒大概? 100,000?reads?before?compaction)

readrandom?:?5.215?micros/op;?( 每秒大概 190,000?reads?after?compaction)

其他信息

實(shí)現(xiàn)相關(guān)

Immutable?table 文件格式

Log 文件格式

譯考文獻(xiàn)

http://leveldb.googlecode.com/svn/trunk/doc/index.html

http://code.google.com/p/leveldb/

http://www.infoq.com/news/2011/07/LevelDB

LevelDB:一個(gè)快速輕量級(jí)的key-value存儲(chǔ)庫(kù)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 始兴县| 慈溪市| 华宁县| 济阳县| 泊头市| 聂拉木县| 上思县| 玉门市| 射洪县| 广平县| 基隆市| 称多县| 淅川县| 巴彦淖尔市| 沁阳市| 介休市| 樟树市| 平凉市| 冀州市| 鹤峰县| 高平市| 平阴县| 松原市| 上饶县| 漯河市| 天等县| 定安县| 内黄县| 菏泽市| 桃源县| 灌云县| 葫芦岛市| 汾阳市| 尚义县| 营口市| 黄平县| 贺州市| 吉林省| 确山县| 英吉沙县| 光泽县|