0.背景
在溝通「商品首頁」展示時,如何保證高性能、高可用,具體來說,3 個方面需要注意:
- 本地緩存
- 分佈式緩存
- 冷數據存儲方案:未命中緩存的冷數據,數據庫併發壓力
1.高併發的緩存實踐
具體來說,圍繞下面 3 項,逐個討論:
- 本地緩存
- 分佈式緩存
- 冷數據存儲方案:未命中緩存的冷數據,數據庫併發壓力
1.1.本地緩存
本地緩存,一般採用 Guava,此時,需要考慮 2 個問題:
- 數據一致性:本地緩存,會導致數據一致性減弱
- 線上服務的影響:大量本地緩存,會導致「堆內存佔用」過多,並且導致 gc,會影響線上服務的執行
業界解決方案:
- TODO
Think:
- JVM 上,本地內存的緩存,分級?
- JVM 上,如何使用「直接內存」?直接內存是否存在安全性問題?(多進程都訪問)
關於堆外內存:
-
JDK 1.8 默認的「堆外內存大小」跟「堆大小」一致,如果沒設置-XX:MaxDirectMemorySize,則默認與-Xmx參數值相同
-
堆外內存:
- 是什麼?JVM 進程佔用的「直接內存」,非堆內存
- 疑問:這個內存,是 JVM 進程獨佔的嗎?是否有其他進程訪問的風險?
- 確定:這個內存本質是受 OS 管理的,其他進程可能會訪問到
- 「堆外內存」:JVM 在直接內存中,開啓的一塊內存區域
- 利用 unsafe 包內工具,直接對物理內存進行的讀寫
- 是「byte 數組」
- Java 對象不能直接保存在裏面,需要經過「序列化/反序列化」實現存取
- 有什麼用?
- 減少 GC 次數:「堆外內存」的佔用,不需要
- 減少複製次數:「堆內數據」發送到遠端時(網絡 IO or 文件 IO),會先複製到「直接內存」再發送,使用「堆外內存」節省了這一步
- 適用場景:
- 長期存在和能複用的場景:「堆外內存」的分配和回收,也是需要成本的
- 注重穩定性的場景:「堆外內存」能有效避免因 GC 導致的暫停
- 適合簡單對象存儲:內部存儲的都是「byte 數組」,Java 對象的讀寫,需要經過「序列化/反序列化」,複雜對象的「序列化/反序列化」需要特別注意
- 適合注重 IO 效率的場景:用「堆外內存」讀寫文件,效率高
- 有什麼問題?
- 內存泄露:對外內存如果泄露,很難排查
- 不適合存儲複雜對象:內部存儲的都是「byte 數組」,Java 對象的讀寫,需要經過「序列化/反序列化」,複雜對象的「序列化/反序列化」需要特別注意
- 如何分配對外內存?
- 分配:本質 unsafe 包,直接操作物理內存,都要轉換爲「字節數組」
- 複雜的 Java 對象,都需要手動進行「序列化/反序列化」,因此,複雜對象不建議存儲在「對外內存」
- 如何實現「堆外內存」的讀寫?
- 有開源方案,實現「堆外內存」的讀寫
1.2.分佈式緩存
分佈式緩存,存在幾個問題:
- 分佈式緩存存儲不足時,如何處理?只有 LRU 清理緩存這一種策略?
- 分佈式緩存,Redis 單實例,能支持最大多少併發連接數?QPS 又是多大?
實踐中,典型問題:
- Redis 單實例的併發連接數,以及如何設置?默認 1w,可以設置爲 1~10w,底層使用 IO 多路複用
- QPS 如何衡量,跟「併發連接數」之間,是什麼關係?1kw
關聯資料:
- Redis 單機的併發連接數:默認設置爲 1w,可以調整 1~10w 之間
- Redis 單機的 QPS 峯值:5w 左右
分佈式緩存的:雪崩、擊穿、併發(併發控制)
- https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md
- http://ningg.top/computer-basic-theory-cache-intro-and-best-practice/
- https://blog.51cto.com/13904503/2165627
Note:
- 併發:大量請求,集中到達,讀取同一個 key,都未命中緩存,都去 DB 中查詢,導致 DB 壓力激增
- 併發控制:本質就是「互斥鎖」例如 Redis 的 setnx,獲取互斥鎖的,其他請求會「本地自旋」查詢「緩存」
- 謹慎使用:針對「熱點數據」需要精細分析業務,然後,謹慎使用「緩存的併發控制」
1.3.冷熱庫存儲
採用 MySQL 存儲數據時,在請求量過大,並且緩存空間有限的情況下,如何考慮「冷數據」的處理:
-
假設首頁的 QPS 20w,每次 50 個商品,緩存命中率 90%
-
在不使用 redis 的 mget(multiGet)
可以使用 NoSQL 方案,例如阿里的 Tair,內存存儲,併發效率更高,具體還需要進一步實踐。
2.參考資料
- 基礎原理系列:緩存通用原理和實踐 http://ningg.top/computer-basic-theory-cache-intro-and-best-practice/
- Spring 源碼:Spring Cache** **http://ningg.top/spring-framework-series-spring-cache/
- 常見性能優化策略的總結
- 聊聊輕量級本地緩存設計:https://toutiao.io/posts/56yqwd/preview
- 如何優雅的設計和使用緩存?(推薦):https://blog.51cto.com/13904503/2165627
- Java緩存深入理解:https://cloud.tencent.com/developer/article/1028722
- 本地緩存(Java實現之理論篇):https://www.jianshu.com/p/866e8455e769
- 緩存那些事:https://tech.meituan.com/2017/03/17/cache-about.html
- https://github.com/ningg/personal
- JVM 堆外內存:
- 從0到1起步-跟我進入堆外內存的奇妙世界:https://www.jianshu.com/p/50be08b54bee
- 關於JVM堆外內存的一切:https://juejin.im/post/5be538fff265da611b57da10
- http://mizhichashao.com/post/4
- 關於java中的本地緩存-總結概述
- 緩存的 3 種模式:緩存更新的套路
- Redis 單機的併發連接數:默認設置爲 1w,可以調整 1~10w 之間
- Redis 單機的 QPS 峯值:2 kw 左右