一、緩存的概念
- 在服務端編程當中,緩存主要是指將數據庫的數據加載到內存中,之後對該數據的訪問都在內存中完成,從而減少了對數據庫的訪問,解決了高併發場景中數據庫容易成爲性能瓶頸的問題;以及基於內存的訪問速度高於磁盤的訪問速度的原理(數據庫讀取數據一般需要從磁盤讀取),提高了數據的訪問速度和程序性能。
- 根據緩存是否與應用進程屬於同一進程,可以將內存分爲本地緩存和分佈式緩存。本地緩存是在同一個進程內的內存空間中緩存數據,數據讀寫都是在同一個進程內完成;而分佈式緩存是一個獨立部署的進程並且一般都是與應用進程部署在不同的機器,故需要通過網絡來完成分佈式緩存數據讀寫操作的數據傳輸。
二、本地緩存
本地緩存的優缺點
1. 訪問速度快,但無法進行大數據存儲
- 本地緩存相對於分佈式緩存的好處是,由於數據不需要跨網絡傳輸,故性能更好,但是由於佔用了應用進程的內存空間,如 Java 進程的 JVM 內存空間,故不能進行大數據量的數據存儲。
2. 集羣的數據更新問題
- 與此同時,本地緩存只支持被該應用進程訪問,一般無法被其他應用進程訪問,故在應用進程的集羣部署當中,如果對應的數據庫數據,存在數據更新,則需要同步更新不同部署節點的本地緩存的數據來包保證數據一致性,複雜度較高並且容易出錯,如基於 Redis 的發佈訂閱機制來同步更新各個部署節點。
3. 數據隨應用進程的重啓而丟失
- 由於本地緩存的數據是存儲在應用進程的內存空間的,所以當應用進程重啓時,本地緩存的數據會丟失。所以對於需要持久化的數據,需要注意及時保存,否則可能會造成數據丟失。
適用場景
- 所以本地緩存一般適合於緩存只讀數據,如統計類數據。或者每個部署節點獨立的數據,如長連接服務中,每個部署節點由於都是維護了不同的連接,每個連接的數據都是獨立的,並且隨着連接的斷開而刪除。如果數據在集羣的不同部署節點需要共享和保持一致,則需要使用分佈式緩存來統一存儲,實現應用集羣的所有應用進程都在該統一的分佈式緩存中進行數據存取即可。
本地緩存的實現
- 緩存一般是一種key-value的鍵值對數據結構,所以需要使用字典數據結構來實現,在 Java 編程中,常用的字典實現包括 HashMap 和 ConcurretHashMap。
- 與此同時,本地緩存由於需要被不同的服務端線程併發讀寫,故需要保證線程安全。由於 HashMap 不是線程安全的,而 ConcurrentHashMap 是線程安全的,故一般會使用 ConcurrentHashMap 來作爲 Java 編程中的本地緩存實現。除此之外,也有其他更加智能的本地緩存實現,如可以定時失效,訪問重新加載等特性,典型實現包括 Google 的 guava 工具包的 Cache 實現,這些也是線程安全的。
三、分佈式緩存
分佈式緩存的優缺點
1. 支持大數據量存儲,不受應用進程重啓影響
- 分佈式緩存由於是獨立部署的進程,擁有自身獨立的內存空間,不會受到應用進程重啓的影響,在應用進程重啓時,分佈式緩存的數據依然存在。同時對於數據量而言,由於不需要佔用應用進程的內存空間,並且一般支持以集羣的方式拓展,故可以進行大數據量的數據緩存。
2. 數據集中存儲,保證數據一致性
- 當應用進程採用集羣方式部署時,集羣的每個部署節點都通過一個統一的分佈式緩存進行數據存取操作,故不存在本地緩存中的數據更新問題,保證了不同節點的應用進程的數據一致性問題。
3. 數據讀寫分離,高性能,高可用
- 分佈式緩存一般支持數據副本機制,可以實現讀寫分離,故可以解決高併發場景中的數據讀寫性能問題。並且由於在多個緩存節點冗餘存儲數據,提高了緩存數據的可用性,避免某個緩存節點宕機導致數據不可用問題。
3. 數據跨網絡傳輸,性能低於本地緩存
- 由於分佈式緩存是獨立部署的進程,並且一般都是與應用進程位於不同的機器,故需要通過網絡來進行數據傳輸,這樣相對於本地緩存的進程內部的數據讀取操作,性能會較低。
分佈式緩存的實現
- 分佈式緩存的典型實現包括 MemCached 和 Redis。
MemCached
- MemCached 相對於本地緩存的主要差別是以獨立進程方式存在,數據集中存儲,數據不隨應用程序的重啓而丟失。而 key-value 鍵值對的 value 也是一個簡單的對象類型,即 value 可以是任意格式的數據,如簡單的數字、字符串、對象等,也可以是文件、圖像、視頻等複雜格式的數據,但是不支持數據結構的特性。
- 所以 MemCached 進程相當於是在內存維護了一個非常大的哈希表來存儲數據,對應的數據操作複雜度都是 O(1),即常量級別,這也是 MemCached 高性能的一個實現方式,鍵值對存取速度都非常快。
Redis
- Redis 是在此基礎上,更一步豐富了key-value 鍵值對的 value 的數據結構類型,即可以在 Redis 中完成 value 的相關數據操作,如 Set 集合去重、有序集合 ZSet 實現數據排序等,這樣就不需要在應用程序額外進行這些操作,實現了開箱即用。並且 Redis 是單線程的,不存在併發數據讀寫的線程安全問題,以及更重要的是保證的數據讀寫操作的順序性。
- 除此之外,Redis 支持主從同步(讀寫分離)、集羣分片拓展、數據持久化等特性,這也是 MemCached 不支持的。所以在高併發場景並且數據能夠容忍極端情況下的少量丟失,或者說丟失後可以恢復,如通過日誌或者重新計算等, Redis 也可以作爲數據庫來使用,提高高併發場景中的訪問性能。