[轉]Ari Zilka談Ehcache的進程內堆外緩存BigMemory
Ehcache的BigMemory提供了一個進程內的堆外緩存,用來存儲應用相關的大批量數據。Terracotta發布了BigMemory模塊的GA版本,該模塊支持Ehcache企業版。BigMemory是Ehcache標準API的一部分,為cache定義兩個新屬性(overflowToOffHeap和maxMemoryOffHeap)就可以使用了,代碼片段如下所示:
<cache name="sample-offheap-cache"
maxElementsInMemory="10000"
eternal="true"
memoryStoreEvictionPolicy="LRU"
overflowToOffHeap="true"
maxMemoryOffHeap="1G"/>
BigMemory在內存存儲策略上有別于傳統的緩存解決方案。它不把數據存儲在Java堆里,從而避免了JVM的GC問題。BigMemory這 種特別的存儲被稱為堆外(Off-Heap)存儲。傳統的緩存解決方案為了規避這些問題,都將數據分布在緩存節點組成的集群上。BigMemory提供了 一種新的架構選擇方案,允許應用運行在堆小于1G的JVM上,利用堆外的內存來加快數據訪問的速度。
InfoQ有幸采訪了Terracotta的CTO Ari Zilka,請他談了Ehcache框架里的BigMemory新特性、BigMemory有助于應用性能提升的用例場景,以及BigMemory的局限性。
InfoQ:為Ehcache框架添加BigMemory特性的主要動機是什么?
主要動機是為了解決我們在Terracotta服務器里遇到的GC問題。服務器里的GC會引起響應時間和大規模GC事件出現變 化,可能會致使(一級緩存L1的)緩存客戶端故障轉移到備份的Terracotta服務器上去。我們意識到這個解決方案的好處后就立即擴大了它的應用范 圍,包括為獨立Ehcache追加一個內存存儲,這個內存存儲后來就發展成了BigMemory——Ehcache企業版的一個插件。
InfoQ:BigMemory堆外存儲提供的方式能避免Java GC的復雜性,你能談一談實現的具體細節么?
BigMemory把緩存對象存儲在Java堆之外,但仍然在操作系統的Java進程里。所以它仍然是個進程內的緩存,具備所有與此相關的高性能,但它不使用堆,這樣就可以給應用配置很小的堆空間,從而避免GC問題。BigMemory使用了JDK 1.4引入的DirectByteBuffers。所有的Java實現都可以運行BigMemory,所以任何人都可以使用BigMemory,而不用更換JDK。操作系統內存管理器的功能差不多就要完成了。屆時我們會在放入數據時分配內存、移除數據時釋放內存,我們能做這些事情是因為BigMemory是個 緩存,而不是一般用途的Java程序。DirectByteBuffers分配內存很慢,但用起來非常快。所以我們會在啟動的時候就從操作系統獲取所有需 要的內存。
BigMemory的關鍵之處在于,怎樣判斷某個對象不再被使用、關聯的內存被釋放了,這也是很多人一開始最難理解的地方。對緩存來說,這其實非常 簡單。Map主要涉及put、get和remove操作。我們在放入數據的時候分配內存(malloc),移除數據的時候釋放內存(free)。我們實現 了一個內存管理器,它使用的是很好理解的計算機科學算法,還有我們專門為此實現的增強。
在被問及BigMemory什么情況下能提升應用性能時(從只讀、經常讀、讀寫操作來說),Ari回答說,在“90%讀/10%寫”的常見情況和“50%讀/50%寫”的寫操作過多的情況下,他們都見過比較好的性能結果。這是因為緩存是進程內的。只讀操作會受到分布式緩存的影響。讀取活躍數據集要比讀取其它數據快很多,那些不活躍的數據必須通過網絡獲取。
InfoQ:BigMemory解決方案有什么局限性?
鑒于BigMemory是純Java的、進程內的,而且和常見的JVM、容器兼容,所以它沒有明顯的局限性。我們找到的最大的內存盒有384GB內存,我們在上面測試的結果顯示,在BigMemory始終有350GB內存的空閑情況下,性能都是線性的,沒有明顯的增長。我們要向用戶強調的限制只有一個,那就是使用堆外存儲的話,放置在BigMemory中的對象必須進行序列化。對通常就存儲在緩存里的那些數據來說,這并不是什么問題。
一旦對象被序列化,在返回Java堆的時候必需反序列化才可以使用。這確實是一筆性能開銷。因此在沒有GC的時候,BigMemory會比堆內存儲慢。但BigMemory還是要比底層可用的存儲快很多,不論是本地磁盤、網絡存儲,還是RDBMS等原本記錄數據的系統。
還應指出的是,序列化/反序列化的性能開銷遠沒有很多用戶想象的那么大。BigMemory已經針對字節緩沖區做了優化,本身也包含一些優化機制, 可以對使用標準Java序列化的對象進行優化。舉例來說,測試版發布之后添加的那些優化機制能使復雜Java對象的性能提升兩倍,使byte數組的性能提 升四倍。Terracotta Server Array正是用byte數組存儲數據的。用自定義的序列化則能進一步減少性能開銷。
InfoQ問Ari,架構師和開發人員在應用中使用BigMemory時應該注意哪些最佳實踐和問題。Ari回答說,任何成功的商業應用都要處理伸縮性問題。緩存是最穩妥、最容易實現的解決方案之一。現在比較新穎的是,不用非得引入緩存集群了。
最好的做法就是用新眼光來審視一下你的性能架構,看你能否從大型的進程內緩存獲益。BigMemory可以讓架構師對服務器和進程密度進行優化,以滿足特定的需求,而不用受制于Java的局限性。最大的問題則是大多數人已經針對Java本身的局限性做了優化。比如說,大多數Ehcache用戶會運行32位的JVM。根據OS的不同,32位 Java的地址空間會是2到4GB不等。所以這些用戶在用Java時就放棄了使用很多內存。應用目前可能都運行在RAM很小的硬件上。所以用戶要是想使用 BigMemory來運行100GB的進程內緩存,可能就意味著要更換新的硬件,即便現在硬件很便宜。
InfoQ:Ehcache框架以后的路線圖是怎樣的?BigMemory有什么特殊的么?
我們正在開發Ehcache和Terracotta的下一個版本(代碼以澳大利亞弗里曼特爾的別稱Freo命名),計劃本月發布測試版。我們計劃在這個版本中添加一系列功能特性和性能增強。比如Ehcache Search,它能讓Ehcache用戶在緩存里進行搜索,就像使用數據庫一樣。Ehcache Search已經發布了測試版,文檔也已經全部可用了。至于BigMemory,我們還在繼續提升性能,同時會增加一系列比較實用的增強,例如提供更多的工具,幫助人們更好地理解最適合他們用例的設置。