天天看點

記憶體資料庫FastDB和SQLite性能測評 - 千島湖

原文連結

一、引言

在很多項目中,經常會碰到這樣的需求,需要對大量資料進行快速存儲、查詢、删除等操作,特别是在一些針對諸如營運商、銀行等大型企業的應用中,這些需求尤為常見。比如智能網中的大量線上并發使用者的資料管理、軟交換平台中的線上資訊互動、寬帶/3G等資料網中線上使用者行為記錄等等。

針對這些情形,我們通常需要選擇高性能的資料庫産品,而且通常需要使用記憶體資料庫,顧名思義,記憶體資料庫指的是所有的資料通路控制都在記憶體中進行,這是與磁盤資料庫相對而言的,磁盤資料庫雖然也有一定的緩存機制,但都不能避免從外設到記憶體的交換,而這種交換過程對性能的損耗是緻命的,目前主流資料庫如SYBASE、ORACLE等都有這種緩存機制,如将特定表綁定一定的緩存,進而在一定程度上改善資料吞吐性能。而記憶體資料庫幾乎可以完全避免這種内外存資料交換的發生,特别是在實體記憶體足夠大的裝置上尤其如此,通常這種資料庫也被稱為主存資料庫(Main Memory DataBase, MMDB)。

二、主存資料庫比較

目前比較知名的商業記憶體資料庫有,ORACLE的TimesTen,MCObject的eXtremeDB、南韓的Altibase等,這些資料庫産品性能都非常的強勁,當然價格也相當的強勁,在非特大型系統建設時,通常讓人望而卻步。于是退而求其次,免費開源記憶體資料庫給了我們第二種選擇。 Berkeley DB,SQLite,MonetDB,FastDB,H2等,不一而足。本文主要針對SQLite和FastDB進行性能測評。

2.1 測試準備

首先,筆者通過對評測資料的調研發現,通常認為,BDB性能不如SQLite,參考“免費的實時資料庫,我們該選誰?—-BerkeleyDB與SQLite評測對比 ”。

上文中還提到,“據說FastDB很快,但資料庫大小不能大于實體記憶體…”,于是筆者對FastDB産生了興趣,從FastDB作者的網站看到關于這點的介紹,并不是說資料庫大小不能大于實體記憶體,而是說資料庫大小超過實體記憶體時,性能與不超過時相比會有一定的降低(降低幅度未作說明,估計是不推薦使用)。幸運地是,目前實體記憶體實在說不上貴,伺服器記憶體在10G之上都是很正常的事情了。是以可以根據具體項目資料量需求來确定是否能使用 FastDB,比如并不是所有的表都需要放在記憶體中。下面即将描述的測試表明,一旦使用FastDB,其性能在免費MMDB産品中絕對可執牛耳。由于已經有人對BDB和SQLite進行過比較,是以下面僅将FastDB與其中的優勝者SQLite進行性能測評。SQLite采用記憶體模式,即打開資料庫使使用“:memory:”參數,此時SQLite不産生資料庫檔案,所有操作都在記憶體中,這一點需要特殊說明,與之不同的是,FastDB有兩種模式,磁盤模式和無盤模式,前者會産生磁盤檔案,後者則與SQLite的記憶體模式相同。

說是測評,其實過程也很簡單,無非是設計測試CASE,編寫測試CODE,輸出測試RESULT,最後做出結論。通常我們認為帶索引的插入耗時相對于查詢和删除來說比較長,是以首先來看插入性能。采用一個簡單的表來完成接下來的所有測試,表中僅包含兩個字段,INTEGER intKey,和VARCHAR strKey。測試平台為Window7 32bit系統(Evaluation Copy 7127),編譯器VC6 SP6。在DELL INSPIRON 640m上運作,CPU為Intel Core 2 CPU T5500 @ 1.66GHZ,記憶體2.5G。

對FastDB(采用磁盤模式),表結構的定義如下:

class _TestTable 

public: 

    db_int8 intKey; 

    char const* strKey; 

    TYPE_DESCRIPTOR((KEY(intKey, INDEXED), KEY(strKey, INDEXED))); 

};

REGISTER(_TestTable);

對SQLite,建表SQL如下:

CREATE TABLE [_TestTable] ( [intKey] INTEGER  NOT NULL PRIMARY KEY, [strKey] VARCHAR(50)  NULL)

2.2 不同僚務模式下的插入性能比較

2.2.1 FastDB磁盤模式

我們首先按照批量事務處理的模式将intKey從1到nRecords(記錄條數),并指定相應的strKey,分别調用相應的接口(均為原始 API)插入到兩張表中,這裡的批量事務處理模式指的是,比如插入10000條記錄,插第一條之前開始事務,最後一條之後結束事務。此時在插入不同數目記錄時的表現分别如下(一萬條、十萬條、72萬條、一百萬條):

批量事務送出:

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 10000 record: 63 ms 

[SQLITE] Elapsed time for inserting 10000 record: 639 ms

E:\intrest\FastDB\PerfTest\Debug>del *.fdb (清除測試生成資料,重新測試,下同。)

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 100000 record: 1186 ms 

[SQLITE] Elapsed time for inserting 100000 record: 6318 ms

E:\intrest\FastDB\PerfTest\Debug>del *.fdb

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 7200000 record: 152460 ms 

[SQLITE] Elapsed time for inserting 7200000 record: 560121 ms

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 1000000 record: 15522 ms 

[SQLITE] Elapsed time for inserting 1000000 record: 67423 ms

從上我們可以看出,在批量事務模式下,FastDB比SQLite的插入性能提高了3-10倍。但是在很多情況下,我們可能會需要逐條逐條的事務送出,下面給出了逐條事務模式的測試結果:

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 10000 record: 57315 ms(這個太恐怖了,不調整的話沒法使用) 

[SQLITE] Elapsed time for inserting 10000 record: 780 ms

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe (SQLITE顯式分條事務) 

[FASTDB] Elapsed time for inserting 10000 record: 59967 ms 

[SQLITE] Elapsed time for inserting 10000 record: 1154 ms

從上我們可以看出,FastDB在這種情形下的性能急遽降低,降到一個幾乎不能接收的水準。經過對FastDB的源代碼分析(開源的好處展現出來了),發現FastDB在每次事務送出時,都會将變更的資料内容同步到磁盤檔案中(這是因為我們采用了磁盤模式),是以造成性能的顯著降低。

直覺上看,解決FastDB的這個問題有兩種辦法,一是避免每次事務送出時同步到磁盤,因為在這種應用中,這種同步操作并不需要實時進行,通常每隔一段時間同步一次就可以了(比如1S、1Min、等根據具體項目的可靠性需要);二是使用前面提到的FastDB無盤(DISKLESS)模式。

我們首先來看第一種方案,通過SEARCH FastDB文檔(文檔和社群是FastDB的一個軟肋),我們發現作者已經考慮到了這個問題,FastDB為資料庫提供了precommit的接口,用于完成除sync到磁盤檔案外的所有事物操作,如釋放mutex資源等。同時提供了backup接口,用來完成記憶體資料到磁盤檔案的備份,甚至支援打開資料庫時同時指定定時備份到磁盤檔案的間隔。這樣一來,每次事務送出的效率理論上會得到大大提高,并且通過定時備份機制可以保證資料的可靠性。我們來看使用 precommit進行逐條事務送出時FastDB的表現:

E:\intrest\FastDB\PerfTest\Debug>PerfTest(使用precommit逐條送出事務) 

[FASTDB] Elapsed time for inserting 10000 record: 62 ms 

[SQLITE] Elapsed time for inserting 10000 record: 1170 ms

E:\intrest\FastDB\PerfTest\Debug>PerfTest 

[FASTDB] Elapsed time for inserting 100000 record: 1170 ms 

[SQLITE] Elapsed time for inserting 100000 record: 11747 ms

E:\intrest\FastDB\PerfTest\Debug>PerfTest 

[FASTDB] Elapsed time for inserting 1000000 record: 8081 ms 

[SQLITE] Elapsed time for inserting 1000000 record: 125768 ms

從上可以看出,在逐條事務模式下,通過使用precommit技術,FastDB性能比SQLite提高了10倍左右。當然也許有讀者懷疑加了備份機制之後的性能,确實筆者沒有進行這項測試,但是,需要注意的是,FastDB在資料庫關閉時會強制sync到磁盤檔案,但SQLite沒有這種功能,同時,在進行這項測試時,兩種資料庫都沒有定時備份機制,是以該比較是公平的。

2.2.2 FastDB無盤模式

再來看第二種方案,FastDB采用無盤(通過編譯選項控制生成DISKLESS版本)模式,此時FastDB初始化一段共享記憶體(shmat or mmap),這個初始大小通常很大,并且運作期不能擴充(無盤模式的劣勢)。我們将初始共享記憶體設定為1G,得到的測試結果如下:

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 100000 record: 624 ms (批量事務送出) 

[SQLITE] Elapsed time for inserting 100000 record: 11544 ms

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 100000 record: 7410 ms (逐條事務送出) 

[SQLITE] Elapsed time for inserting 100000 record: 11560 ms

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 1000000 record: 134660 ms 

[SQLITE] Elapsed time for inserting 1000000 record: 120167 ms

E:\intrest\FastDB\PerfTest\Debug>PerfTest.exe 

[FASTDB] Elapsed time for inserting 250000 record: 23666 ms 

[SQLITE] Elapsed time for inserting 250000 record: 29110 ms

從上我們可以看出,無盤模式在大資料量下的表現與SQLite相近,這一點不是很好了解,需要研究DISKLESS的設計模式,理論上應該與 precommit模式性能相近。但是實踐是檢驗真理的唯一标準。我們可以看出,磁盤模式的precommit方式性能表現卓越,不管從橫向還是縱向來看。

2.3 查詢性能比較

下面的比較都使用磁盤模式的precommit方式,再來看索引查詢的性能表現,測試時都是先插入十萬條資料後,再分别對該十萬條資料進行查詢,需要注意的是我們同時對FastDB是否增加HASH索引的性能進行了橫向測評,FastDB增加HASH索引很簡單,通過修改TYPE- DESCRIPTOR來完成,上面的class中改為TYPE_DESCRIPTOR((KEY(intKey, INDEXED), KEY(strKey, INDEXED)));即為intKey增加了Hash索引。

E:\intrest\FastDB\PerfTest\Debug>perftest (FASTDB哈希索引) 

[FASTDB] Elapsed time for inserting 100000 record: 624 ms 

[FASTDB] Elapsed time for 100000 index searches: 328 ms 

[SQLITE] Elapsed time for inserting 100000 record: 10312 ms 

[SQLITE] Elapsed time for 100000 index searches: 10935 ms

E:\intrest\FastDB\PerfTest\Debug>perftest(FASTDB非哈希索引) 

[FASTDB] Elapsed time for inserting 100000 record: 577 ms 

[FASTDB] Elapsed time for 100000 index searches: 515 ms 

[SQLITE] Elapsed time for inserting 100000 record: 10343 ms 

[SQLITE] Elapsed time for 100000 index searches: 9532 ms

從測試結果可以看出,查詢十萬條索引記錄的效率,FastDB要比SQLite快20倍左右,并且在增加HASH索引後能夠得到進一步的改善。

2.4 删除性能比較及綜合表現

最後,我們在測試删除效率時,同時綜合來看FastDB與SQLite之間插入、查詢、删除的性能表現:

插入、查詢、删除綜合比較:

E:\intrest\FastDB\PerfTest\Debug>perftest(批量删除,FASTDB.removeall(),SQLITE.delete*)

[FASTDB] Elapsed time for inserting 100000 record: 608 ms 

[FASTDB] Elapsed time for 100000 index searches: 687 ms 

[FASTDB] Elapsed time for deleting all 100000 records: 16 ms 

[SQLITE] Elapsed time for inserting 100000 record: 11107 ms 

[SQLITE] Elapsed time for 100000 index searches: 10062 ms 

[SQLITE] Elapsed time for deleting all 100000 records: 16 ms

E:\intrest\FastDB\PerfTest\Debug>perftest(逐條删除) 

[FASTDB] Elapsed time for inserting 100000 record: 593 ms 

[FASTDB] Elapsed time for 100000 index searches: 562 ms 

[FASTDB] Elapsed time for deleting all 100000 records one by one: 905 ms 

[SQLITE] Elapsed time for inserting 100000 record: 10406 ms 

[SQLITE] Elapsed time for 100000 index searches: 10249 ms 

[SQLITE] Elapsed time for deleting all 100000 records one by one: 8923 ms

從上可以看出,就删除效率而言,批量删除的速度二者相近,而逐條删除時,十萬條記錄的删除累積,FastDB比SQLite快了10倍左右。

2.5 總結

優點:FastDB磁盤模式下,采用precommit方式,性能遠遠優于SQLite,并且FastDB提供了完善的備份恢複機制,能夠保證資料安全。FastDB的無盤模式在小資料量時表現優越,并且不會産生磁盤資料檔案,也不能加載已經儲存的資料庫檔案,看起來更像是針對嵌入式裝置(如智能手機、PDA等)開發的,對于這種場景可以考慮使用無盤模式。

缺點:FastDB目前能夠SEARCH到的比較著名的應用是PingTel公司的開源統一通信産品SIPX,該産品采用的是FastDB的磁盤模式。這可能多少與FastDB的完全授權模式有關,而SQLite采用的是GPL的不允許閉源的商業釋出。當然主要還是社群的不成熟,這從Google Trends的搜尋結果也能看出。社群的不成熟會帶來學習成本的增加,這一點在選型時也需要考慮。

記憶體資料庫FastDB和SQLite性能測評 - 千島湖