天天看點

OceanBase分布式存儲引擎公共子產品——記憶體管理OceanBase分布式存儲引擎公共子產品——記憶體管理

記憶體管理是c++高性能伺服器的核心問題。一些通用的記憶體管理庫,比如google tcmalloc,在記憶體申請/釋放速度、小記憶體管理、所開銷等方面都已經做得相當卓越了,然而,我們并沒有采用。這是因為,通用記憶體管理庫在性能上畢竟不如專用的記憶體池,更為嚴重的問題是,它鼓勵了開發人員忽視記憶體管理的陋習,比如在伺服器程式中濫用c++标準模闆庫(stl)。

在分布式存儲系統開發初期,記憶體相關的bug相當常見,比如記憶體越界、伺服器出現core comp,這些bug都非常難以調試。是以,這個時期記憶體管理的首要問題并不是高效,而是可控性,并防止記憶體碎片。

oceanbase系統有一個全局的定長記憶體池,這個記憶體池維護了由64kb大小的定長記憶體塊組成的空閑連結清單,其工作原理如下:

如果申請的記憶體不超過64kb,嘗試從空閑連結清單中擷取一個64kb的記憶體塊傳回給申請者;如果空閑連結清單為空,需要首先從作業系統中申請一批大小為64kb的記憶體塊加入空閑連結清單。釋放時将64kb的記憶體塊加入到空閑連結清單中以便下次重用。

如果申請的記憶體超過64kb,直接調用glibc 的記憶體配置設定(malloc)函數,向作業系統申請使用者所需大小的記憶體塊。釋放時直接調用glibc的記憶體釋放(free)函數,将記憶體塊歸還作業系統。

oceanbase的全局記憶體池實作簡單,但記憶體使用率較低,即使申請幾個位元組的記憶體,也需要占用大小為64kb的記憶體塊。是以,全局記憶體池不适合管理小塊記憶體,每個需要申請記憶體的子產品,比如updateserver中的memtable,chunkserver中的緩存等,都隻能從全局記憶體池中申請大塊記憶體,每個子產品内部再實作專用的記憶體池。每個線程會緩存若幹個大小分别為64kb和2mb的記憶體塊,每個線程總是首先嘗試從線程局部緩存中申請記憶體,如果申請不到,再從全局記憶體池中申請。

obiallocator 是記憶體管理器的接口,包含alloc和free兩個方法。obmalloc和obtcmalloc是兩個實作了obiallocator接口的全局記憶體池,不同點在于,obmalloc不支援線程緩存,obtcmalloc支援線程緩存。obtcmalloc首先嘗試從線程局部的空閑連結清單申請記憶體塊,如果申請不到,在通過obmalloc的alloc方法申請。釋放記憶體時,如果沒有超出線程緩存的記憶體塊個數限制,則将記憶體塊還給線程局部的空閑連結清單;否則,通過obmalloc的free方法釋放。另外,允許通過set_mod_id函數設定申請者所在的子產品編号,便于統計每個子產品的記憶體使用情況。

全局記憶體池的意義如下:

全局記憶體池可以統計每個子產品的記憶體使用情況,如果出現記憶體洩漏,可以很快定位到發生問題的子產品。

全局記憶體池可用于輔助調試。例如,可以将全局記憶體池中申請到的記憶體塊按位元組填充為某個非法的值(比如0xfe),當出現記憶體越界等問題時,伺服器程式會很快在chu'xian'wen'ti'd出現問題的位置core dump,而不是帶着錯誤運作一段時間後才core dump,進而友善問題定位。

總而言之,oceanbase的記憶體管理沒有采用高深的技術,也沒有做到通用或者最優,但是很好的滿足了伺服器程式開發的兩個最主要的需求:可控性以及沒有記憶體碎片。

繼續閱讀