Tair mdb 存儲引擎的實現
來自: http://www.0xffffff.org/2015/03/28/35-Tair-mdb-analyse/
Tair是一個高性能、分布式、可擴展、高可靠的NoSQL存儲系統。本文基于Tair v3.1.2.43版本,探究其mdb存儲引擎的實現。
Tair目前有mdb、ldb和rdb等存儲引擎。其中mdb是Tair最早的一款內存型產品,也是在公司內部應用最廣泛的集中式緩存。特別適用容量小(一般在M級別,50G之內),讀寫QPS高(萬級別)的應用場景。由于是內存型產品,因此無法保證數據的安全性,對數據安全有要求的應用建議在后端加持久化數據源(例如MySQL)。本文接下來詳細討論Tair mdb存儲引擎的實現。
Tair的存儲引擎接口是src\storage\storage_manager.hpp
里的虛基類storage_manager
。所有的Tair存儲引擎均繼承實現了storage_manager
這個虛基類。src\dataserver\tair_manager.cpp
文件中的tair_manager::initialize
函數根據配置文件中storage_engine
的設置初始化相應的存儲引擎。
mdb引擎默認使用POSIX共享內存的方式進行內存的分配和管理。mdb引擎會在初始化的時候創建或者使用已存在的共享內存。其配置使用內存的方式和共享內存命名的前綴均在其配置文件中進行設置。如圖所示:
src\storage\mdb
目錄是mdb存儲引擎的實現,這里的實現、測試和接口文件都放在同一個目錄中。其中有關mdb存儲實現的文件如下:
◇ db_define.{hpp,cpp} —— mdb引擎相關的配置信息和定義。 ◇ mdb_factory.{hpp,cpp} —— mdb引擎初始化工廠類的實現。 ◇ mdb_manager.{hpp,cpp} —— mdb引擎管理結構的實現。 ◇ mdb_instance.{hpp,cpp} —— mdb實例相關的實現。 ◇ mem_cache.{hpp,cpp} —— MemCache結構的實現。 ◇ mem_pool.{hpp,cpp} —— MemPool結構的實現。 ◇ cache_hashmap.{hpp,cpp} —— 全局緩存KV結構映射的Hash表的實現。 ◇ mdb_stat_manager.{hpp,cpp} —— mdb引擎狀態管理相關實現。 ◇ mdb_stat.hpp —— mdb引擎讀取寫入統計的相關數據結構的定義和實現。 ◇ lock_guard.hpp —— pthread_mutex_t的簡單RAII封裝。
其中mdb_define.{hpp,cpp}
里定義了mdb引擎的配置變量,打開共享內存的操作函數以及獲取時間、判斷當前時間的hour是否在給定區間等函數。
mdb_factory.{hpp,cpp}
用于創建mdb引擎,mdb_factory.hpp
中定義的接口如下:
create_mdb_manager讀取配置文件中的配置信息,然后創建mdb_manager
對象并返回。這個函數在src\dataserver\tair_manager.cpp
文件中的tair_manager::initialize
函數中被調用。
mdb_manager.{hpp,cpp}
定義了mdb引擎管理類mdb_manager
的實現,mdb_manager
類繼承自storage_manager
虛基類,實現了相關的虛函數接口。其定義std::vector<mdb_instance*>
結構保存所有的mdb實例。mdb_instance
類在mdb_instance.{hpp,cpp}
中定義和實現。mdb_manager
類的initialize
函數會調用init_area_stat
函數創建/打開名為mdb_param::mdb_path+".stat
這個存儲mdb引擎狀態信息的共享內存塊,其大小為TAIR_MAX_AREA_COUNT * sizeof(mdb_area_stat)
。然后會根據配置文件里的實例個數信息創建mdb_instance
實例。配置如下:
mdb_instance
創建時會創建名為mdb_param::mdb_path+".000"
開始計數的共享內存塊。創建的實例中bucket的個數由以下配置決定:
mdb_instance
、mem_cache
、mem_pool
、cache_hashmap
這幾個類構成了mdb存儲引擎的核心。創建完成后,其包含指向關系如下:
對應的內存結構圖大致如下:
其中MemPool以頁的形式管理通過共享內存分配的內存,分配或者釋放一個內存頁。其定義了uint8_t page_bitmap[BITMAP_SIZE]
,以位的形式來管理內存頁;MemCache比頁低一級,采用slab算法將內存分配給具體的item;HashTable以一個巨大的Hash表存儲key的映射關系。下面闡述對共享內存具體的分配情況。內存布局如圖所示:
cache meta的結構如下:
hash buckets的結構如下:
slab use這里,當前的代碼實際上僅放置了一個mdb_cache_info:
下面是內存管理結構中的一些定義:
mdb_id
的定義:
其中item_id
的圖示如下:
一些換算關系如下:
page_addr = S0 + (page_id * page_size) item_addr = S0 + (page_id * page_size) + sizeof(page_info) + (slab_size * offset_in_page)
最后是slab分配器和K/V存儲相關的細節。mem_cache
類使用slab_manager
類對從mem_pool
中申請到的內存頁進行管理。頁信息的結構定義如下:
mem_cache
里的pages被放置在三個鏈表中,分別是Free頁鏈表、Full頁鏈表和Partial頁鏈表。Free頁鏈表、Full頁鏈表是簡單的雙向鏈表,用于鏈接空頁和滿頁。Partial頁鏈表如下圖:
下圖是不同的Area里放置item的圖示:
最后剩下的mdb_stat_manager.{hpp,cpp}
和mdb_stat.hpp
定義和實現了訪問統計相關的功能,其創建的共享內存塊為相應的mdb實例的名稱+”mdbstat”,此處不再贅述。