【編者按】雖已問世9年之久,但是相較MongoDB,Hamsterdb的知名度仍然有所欠缺,更一度被評為非主流數據庫。Hamsterdb是個開源的鍵值類型數據庫。但是區別于其他NoSQL,Hamsterdb是單線程和非分布式的,其特性設計也更像是一個列存儲數據庫,同時還支持read-committed隔離級別的ACID事務。那么對比LevelDB,Hamsterdb又會有什么優勢,這里我們走進項目參與者之一Christoph Rupp的分享。
以下為譯文:
在這篇文章中,我想向大家介紹Hamsterdb――一個基于Apache 2-licensed協議的嵌入式分析型鍵值數據庫,類似于谷歌的LevelDB和甲骨文的BerkeleyDB。
Hamsterdb并不是一個新的競爭者。事實上,Hamsterdb已經問世了9年。這一次,它成長得很快,并且專注于鍵值存儲的數據庫分析技術,類似于列存儲數據庫。
Hamsterdb是單線程和非分布式的,它可以直接連接到用戶應用程序中。Hamsterdb提供一種獨特的事務實現,以及有類似于列存儲數據庫所具備的特性,非常適合于分析型工作負載。它可以被C/C++原生調用,也面向Erlang、Python、Java、NET、甚至是Ada等編程語言。同時,它還在嵌入式設備和前置應用程序中得到了上千萬的部署,以及服務于云端――例如高速緩存和索引,已經有數以百萬計的部署。
Hamsterdb在鍵值存儲中有一個獨特的功能:它能識別架構信息。盡管大多數數據庫無法分析出或關注被插入鍵類型,但是hamsterdb支持兩種類型的鍵值:binary key(固定長度VS.可變長度)和numerical key鍵(比如uint32、uint64、real32、real64)。
Hamsterdb的數據庫也是被存儲在文件或存儲器的Btree索引。使用Btree讓hamsterdb的分析能力變得強大。Btree索引應用了C++模塊,該模塊參數取決于鍵類型和日志的大?。ü潭ㄩL度vs.可變長度),與鍵是否重復無關,因而每一個Btree節點對于工作負載來說是高可用的。因為鍵的定長,所以每一個鍵是零負載的,而且鍵被排列得像簡單數組。在聚焦索引的最底層,uint64鍵數據庫支持 uint64_t類型的C數組。
這種實現減少了I/O并更加有效地利用了CPU緩存。當今的CPU需要對內存性能進行優化處理,這也是Hamsterdb的一大優勢。例如,通過搜索葉節點時,二進制搜索在可用內存達到一定閥值時會被跳過,取而代之的是線性搜索。而且,hamsterdb具有等價于SQL commands COUNT、COUNT DISTINCT、SUM 和AVERAGE的API,鑒于直接在Btree上運行,使得它能夠快速地工作在固定長度的鍵上。
Hamsterdb也支持可變長度的鍵。因此,每個Btree節點有一個非常小的索引提前指向節點的有效負載。這可能導致現有鍵長度調整或刪除后的重組,因此必須對節點做“抽空(vacuumized)”操作,從而避免浪費空間。這個操作會成為一個性能殺手,在速度提升上面臨著巨大挑戰,因此能少用則少用。
Hamsterdb允許副本鍵,這意味著某個鍵可能指向多條記錄,鍵的所有記錄都會被組織在一起。他們可用于處理可變長度鍵的索引結構。(如果一個鍵有許多備份記錄,他們將被從Btree中刪除并存儲于獨立的溢出區)
Hamsterdb支持read-committed隔離級別的ACID事務。事務更新可作為delta-operations存儲于存儲器中。每個數據庫都會有獨立的事務索引,這些事務中的更新比BTree中有著更高的優先級。中止事務只意味著放棄事務索引中該事務的更新,并把事務更新交給Btree。
獨特的設計選擇帶來強大的優勢。事務升級留在RAM中而不是請求I/O。不再需要事務終止邏輯,因為事務一旦被終止就不會繼續執行?;謴瓦壿嬍褂昧艘环莺唵蔚倪壿嬋罩荆泊嬖谥粋€重要挑戰:運行時,兩個樹必須被合并。想象使用數據庫cursor 去完成一個全面掃描,這樣的結果是非常復雜的。一些鍵存在于Btree中,一些在事務樹里。在事務樹中,Btree里的鍵可以被重寫或者刪除,甚至會存在被其他鍵修改的情況。因此,在涉及到多個鍵時,這點非常困擾。
Hamsterdb最強力的特性是可測試性。數據庫的基本準則是不能丟失數據,這點比性能尤為重要。關鍵性的Bugs都可被解決的。另外,在九年的開發過程中,為了解決技術負債問題,那些在特定情況下出現的拙劣設計基本上被祛除了,正以敏捷、快速反應的姿態響應用戶的需求和新理念,我一直在重寫部分代碼或者嘗試新的想法。高可測試覆蓋給了我很大的信心,因為我的更改不會破壞任何東西。
專注于可測試性和高度自動化使我處理好很多事情。在最糟糕時,Hamsterdb debug充斥大量的assert和完整性檢查,大約有1800個單元測試和35000個驗收測試。那些驗收測試中運行著幾十個不同的結構,并在BerkeleyDB中并行執行。我們會持續檢查兩個數據庫中數據的一致性,所以任何新進bug都會被馬上顯示出來。另外,每個測試都會給出一個細節明細表,包括內存消耗、堆分配數目、被分配頁數目、Blobs(二進制大對象存儲)、btree 的分裂和合并等。
有些測試可以使用valgrind。我們會對比valgrind使用前后的性能,從而快速找出問題發生的地方,并做性能修復。
另外,通過測試模擬數據庫崩潰可測試hamsterdb的可恢復性。最后但同樣值得關注的是,我可以使用 QuviQ的QuickCheck,一個基于Erlang語言的性能檢測工具。QuickCheck讓你得知軟件的性能情況,然后運行pseudo-randomized指令,不斷的核查完整性。
靜態代碼分析可與Coverity的開源產品和clang的scan-build工具一起使用。他們能發現一些細枝末節問題。
在發布前,所有的測試都是全自動和高性能的。一個完整的發布周期通常要花上幾天,且每兩個月都要用掉一個硬盤。
總結我學到的知識,測試編寫將是一件非常有趣的事情。沒有可靠性測試的迭代開發是無法簡化的。
我也來介紹下hamsterdb的商業版本 Hamsterdb pro,該版本針對鍵、記錄和日志提供了重度壓縮 (zlib、snappy、lzf和lzo),以及AES加密和針對葉節點查找的SIMD優化。還有更多的壓縮算法( bitmap compression和prefix compression)正在進行或計劃中。網頁上有更多的信息。
到目前為止尚不錯,但hamsterdb的性能到底如何?我用谷歌的基準測試將Hamsterdb 2.1.8與LevelDB 1.15作了性能對比。壓縮被禁用(Hamsterdb 暫未提供壓縮,但是Hamsterdb pro提供了)。Fsyncs同樣被禁止,它是hamsterdb的一個修復功能(通過預寫日志實現)。測試大小范圍從較小的鍵/記錄到具有中等大小鍵和較大的記錄,并且插入數據,范圍從100K到100M級別。另外,我運行了兩個Hamsterdb 的分析函數,LevelDB也是。所有測試運行的緩存大小從4MB到1GB,機器配備一個HDD和一個SSD。
Hamsterdb的配置總是基于定長鍵――為8字節鍵hamsterdb存儲的uint64 numbers。自從LevelDB需要number轉換成string后,這也就成為了hamsterdb的優勢之一。
我也還增加了較小記錄(size 8)的測試,因為它們含有主鍵時,通常會被用于輔助索引。兩個機器分別使用了不同硬盤:HDD(Core i7-950 8核和8MB緩存)和一個SSD(Core i5-3550 4核和8MB緩存),下面是部分基準測試結果,詳情可以看這里。
持續寫;鍵大?。?6;日志大?。?00(HDD,1 GB緩存)
連續讀;鍵大?。?6;日志大?。?00(HDD,1GB緩存)
隨機寫;鍵大?。?6;日志大?。?00(HDD,1GB緩存)
隨機讀;鍵大?。?6;日志大小:100(HDD,1GB緩存)
計算所有鍵的綜合(HDD,4MB緩存)
計算末尾是“77”的鍵(SSD,1GB緩存)
對于隨機讀,Hamsterdb的性能要好于LevelDB。對于隨機寫,只要數據量不是太大的時候,Hamsterdb 要快于LevelDB。而從1千萬鍵以上開始,hamsterdb就會遭受BTree數據庫的傳統問題:大量的非序連續I/O的高磁盤尋道延遲。
話雖這么說,這個測試也很好地證明了Hamsterdb的分析能力。尤其是sum和count運算都可以很好地擴展。連續插入和掃描也是Hamsterdb的亮點,且不管數據量多大,它都可以非常快。
未來的工作
這個基準測試讓我們發現了很多問題:通過并行hamsterdb,優化隨機讀/寫。這將成為我工作的主要部分,而且我已經草擬一個設計方法,以及在產品發布前進行重構。
原文鏈接: Hamsterdb: An Analytical Embedded Key-Value Store(翻譯/童陽 責編/仲浩)