最近做一個比較大的電子商務項目,預計每天訂單量將在5萬多單,客服人員需要頻繁的下單、查詢訂單、操作訂單,客人預訂完訂單后,會立即進入處理流程,為了提高服務質量,要求流水化作業,平均要在40分鐘-80分鐘內處理完訂單。所以訂單在創建后,會在短時間內,被頻繁的修改和查看。
由于在項目中ORM層主要是基于Hibernate框架,所以在調優時,很自然的就想到打開Hibernate的二級緩存,以次來減小由于Load 訂單大對象時N+1次查詢給數據造成的壓力,自己做的測試效果也非常好,也順利通過壓力測試。
但在上線時,性能卻并不佳,經過分析業務的操作特點,查找原因有以下幾占:,
??? 1.但由于中臺每天在工作當中,頻繁的批量分配工單,
???
??? 因為要批量將訂單分配給某一個工作人員處理,在代碼當中執行了一個bulkUpdate的操作:
template.bulkUpdate("update order set owner = ? where id in (?, ?,?)");
???
??? 這時Hibernate會直接將Order對象的二級緩存清楚掉。
??? 由于二級緩存,總是被刷掉,再查詢時,需要重新從數據庫Load,所以二級緩存變相直接起的作用很少。
??? 2.由于工作人員在處理訂單時,每一次查看之后,都有更新操作,在更新之后,訂單被清除緩存,下一組人在處理訂單時,又得重新LOAD,所以效果并不好。
??? 3.無論是白盒測試,還是壓力測試時,所基于的案例太過于簡單,沒有更深入的模仿業務操作,對于壓力測試的腳本,也很難精確的模擬出真實的流程化的業務操作過程。
??? 開始想到,直接獲得Session,直接使用JDBC來編寫更新代碼,并在更新后,使用sessionFactory.evict(Order.class, id);來有目標的逐個清除特定的對象,以避免全部清楚緩存。
??? 但樣做,會對DAO層,修改過大。
??? 由于整個模塊最核心的商業對象就是訂單,最后決定在Service層對訂單開小灶,對訂單緩存的單獨的定制處理。
??? 我覺得應用緩存存在以下優點:
??? 1。速度要快于ORM緩存,
??? 2。對于緩存的控制權更大,可以直接控制緩存工具的API進行操作,可以避免一些盲目清除的操作。
??? 3。更靈活的控制緩存中對象的失效,如根據事件來清除緩存,如訂單的處理流程結束時,將該訂單從緩存中清除掉,
??? 4。在更新數據庫時,不是直接清除緩存,而是更新緩存(盡管這有風險),當業務層拋出異常時,才去清空緩存,避免由于頻繁更新,而清空緩存。
?? 缺點:
?? 1。訂單的更新操作,必須是單點的,只能通過IOrderService提供的接口,進行更新操作,否則數據不一致的風險較大。
?? 2。有一定的代碼工作量
?? 3。如果不對第三方緩存包,進行一定的封裝的話,會直接耦合于第三方的緩存包,不能像Hibernate那樣,靈活選擇和配置緩存工具。不容易達到ORM緩存最強大的那種透明化和靈活可配置,你可以使用Ehcache, 也可以選Jboss,有錢的話,可以用Tangosol。
?? 4。對業務層代碼有一定的侵入
???
??? 目前的方案是采用應用層的現代化,同時使用如Proxy模式來提供透明化的設計,
??? IOrderService -》? OrderService -》 CacheableOrderService
??? 通過Spring的Bean配置,一樣可以實現透明化的操作。
?? 結論:
?? 1。緩存的清空與更新,要盡量精確的去操作受到更新影響的對象,而不是全部搞掉。
?? 在Hibernate當中,也提供了sessionFactory.evict(class, id)這樣細粒度的清空緩存對象的方法。
????????? sessionFactory.evice(class)的操作,要看這樣的操作是否頻繁,如果頻繁,對于緩存的作用就會大大的折扣。
2。如果緩存對象過多,對于失效的算法與處理,要與業務對象的特性緊密的聯合起來,通過事件來驅動對象的失效。
3。對于商業對象的緩存,必須要深刻分析對象的生命周期,業務特性。
4。對于數據不一致的風險,要有足夠的認識與預防手段。
5。合理的估計訂單對象的大小,分配足夠的內存
6。如果只使用中心緩存,只能減小數據庫的壓力,對于網絡帶寬的壓力,還是有的,速度上也遠遠遜于本地緩存的效果,所以要結合本地緩存+中心緩存的策略方案,即提高速度,避免群集復制時的瓶頸。