- 高性能Java架構:核心原理與案例實戰
- 張方興編著
- 2963字
- 2021-10-15 18:26:08
1.11 緩存的核心知識
緩存是為了減少數據庫和服務器壓力而產生的,在應用層編程時需主要考慮以下幾種情況:
? 客戶端緩存。
? 服務端緩存。
? 網絡緩存(CDN緩存)。
客戶端緩存負責減輕服務端的存儲和頻繁的數據請求等壓力。例如,在QQ初始階段,只有“會員”才可以把QQ表情存儲在“云端”之上,因為騰訊內部并沒有龐大的存儲系統存儲大量的QQ表情。雖然現在騰訊已經取消了只有“會員”才可以存儲QQ表情的限制,但是大部分QQ表情仍然默認存儲在本地客戶端。客戶端緩存大致可分為以下幾種:
? 客戶端本地文件緩存,包括圖片、.txt文件、.doc文件等。
? 客戶端本地HTTP、cookie等瀏覽器緩存。
? 客戶端注冊表。
? 客戶端微型數據庫(SQLite)。
? 客戶端本地計算機內存。
服務端緩存主要是為了減少數據庫壓力和外部服務接口的壓力,這也是實際編程中最常用的手段。除減少數據庫的壓力外,緩存返回數據的響應速度比數據庫要快。另外,盡可能不調用外部接口,因為外部接口無論WebSocket、WebService,還是HTTP,其響應速度都是不可控的。如果外部接口響應時間過長,也會影響自身性能。服務端緩存大致分為以下幾種:
? 容器緩存,如Tomcat、Nginx、JBoss、Servlet等。
? 中間件緩存,如MongoDB、Elasticsearch、Redis、RocketMQ、Kafka、ZooKeeper等。
? JDK緩存,如磁盤緩存、堆內緩存、堆外緩存等。
? 頁面靜態化緩存,如FreeMaker、Thymeleaf等。
? 文件管理,如FastDFS等。
1.11.1 緩存的命中率
緩存的命中率指的是“緩存查詢的次數”與“總查詢次數”的比值。在多級緩存下,可以調研每一級緩存的命中率,以便調整代碼。若某緩存命中率過低,則很可能是緩存穿透問題。
1.11.2 緩存回收方式
? 基于時間:當某緩存超過生存時間時,則進行緩存回收。或者當某緩存最后被訪問后超過某時間仍然沒有被訪問,則進行緩存回收。
? 基于空間:當緩存超過某大小時,則進行緩存回收。
? 基于容量:當緩存超過某存儲條數時,則進行緩存回收。
? 基于引用:軟引用和弱引用緩存會在JVM堆內存不足時進行緩存回收。
1.11.3 緩存回收策略
? 先進先出(First In First Out,FIFO):一種簡單的淘汰策略,緩存對象以隊列的形式存在,如果空間不足,就釋放隊列頭部的(先緩存)對象,一般用鏈表實現。
? 最近最久未使用(Least Recently Used,LRU):是根據訪問的時間先后進行淘汰的,如果空間不足,就釋放最久沒有被訪問的對象(上次訪問時間最早的對象)。
? 最近最少使用(Least Frequently Used,LFU):根據最近訪問的頻率進行淘汰,如果空間不足,就釋放最近訪問頻率最低的對象。
1.11.4 緩存的設計模式
(1)Cache Aside模式:首先讀取緩存中的數據,若緩存沒有命中,則讀取DB。當DB需要更新時,直接刪掉緩存中的數據。由于實現簡單,因此是最常用的一種設計模式,適用于讀操作多的情況。
(2)Read/Write through模式:在讀取時先到緩存中查詢數據是否存在。如果存在,則直接返回。如果不存在,則由緩存組件負責從數據庫中同步加載數據,此數據永不過期。在寫入時,先查詢要寫入的數據在緩存中是否存在。如果存在。則更新緩存中的數據,并且由緩存組件把數據同步更新到數據庫中。Read/Write through模式初步屏蔽了底層數據庫操作,但是當把數據從緩存組件寫入DB時,有可能出現異常無法正確寫入的情況。因而需要謹慎記錄時間戳,以便跟蹤維護處理數據。該方案適合對持久性要求較低的業務場景。
(3)Write Behind Caching(Write Back)模式:Write Behind Caching模式屬于Read/Write through模式的進階版,完全不考慮DB,增刪改查全部通過緩存進行處理。如果讀取不到數據,則直接認為該數據不存在,服務器會定期把緩存中的數據存儲到DB中。一般高并發應用程序最常用的是Write Behind Caching設計模式,它是性能最好的設計模式,但是實現較為復雜,一旦服務器宕機則有可能導致大量數據丟失。
1.11.5 緩存測試應涵蓋的內容
(1)當前程序是否有可能出現緩存穿透、緩存擊穿、緩存雪崩等常見問題。
(2)緩存是否設置了最大位數及時間等功能,是否會出現內存溢出的現象。
(3)緩存能夠節省各數據源多少比重的讀取,例如進程內緩存節省了多少讀取Redis的比重,Redis緩存節省了多少讀取磁盤緩存的比重,磁盤緩存節省了多少讀取MySQL的比重。
(4)App在無網或弱網環境下,是否可以正常打開及使用。例如網易云音樂在沒有網絡的情況下可以聽一些本地緩存的歌曲。
(5)App在弱網轉正常網絡之后,緩存是否能被正常覆蓋。
(6)各級緩存與數據庫是否能夠保持數據一致性,是否包含臟讀、不可重復讀等相關問題。
(7)緩存是否能夠被手動刪除或刷新,若遇到緊急狀況是否能夠進行可逆性操作。
(8)緩存的回收策略、回收方式等內容是否正常生效。
1.11.6 實戰:秒殺系統設計方案
秒殺系統設計要解決的問題如下:
? 突發性大量接口請求導致服務器高負載,此時需要用限流和削峰的方案進行處理。
? 如果突然增加的帶寬超過服務商提供的帶寬上限,則要注意數據傳輸的完整性,即從客戶端向服務器傳輸數據時即使速度緩慢,也要保證數據的完整性,以免數據丟失導致相應的錯誤,此時需要用隊列及分布式鎖等方案進行處理。
? 秒殺時應通過減庫存操作維持數據的一致性,以免造成重復下單(超買/超賣)、庫存不足等現象。此時需要用網關及隊列等方案進行處理。
? 在秒殺之前按鈕應為灰色,之后在不刷新頁面的情況下將按鈕點亮。此處盡量隱藏URL,并對通信信息進行加密處理,以限制各種腳本請求,盡可能按F12鍵后查看不到各種地址及相關信息。
? 控制刷新頁面,當用戶即將參與秒殺時通常會不斷按F5鍵刷新頁面,重新加載頁面同樣會請求接口,應減少此種接口的請求,并將部分數據緩存至客戶端,減輕服務器的壓力。
秒殺系統需要達到限流、削峰、異步處理、高可用、緩存、可擴展等要求,具體如下:
? 限流:鑒于只有少部分用戶能夠秒殺成功,所以要限制大部分流量,只允許少部分流量進入服務后端。常見的限流有單一端口登錄(同一賬號只能單一端口登錄,例如App與Web只允許一個端口正在登錄)、只有登錄賬號才能參與秒殺、當單擊按鈕次數過多時應限制單擊次數,例如,每個賬號每秒只能單擊3次等不同的限流方案。
? 削峰:因為秒殺系統瞬時會有大量用戶涌入,所以在搶購一開始會出現瞬間峰值。高峰值流量是壓垮系統的主要原因之一,如何把瞬間的高流量轉變成一段時間的平穩的流量是設計秒殺系統很重要的思路。對流量進行削峰的解決方案是用消息隊列緩沖瞬時流量,把同步的直接調用轉換成異步的間接推送,中間通過一個隊列在一端承接瞬時的流量洪峰,在另一端平滑地將消息推送出去。消息隊列中間件主要解決應用耦合、異步消息、流量削峰等問題。常用的消息隊列有ActiveMQ、RabbitMQ、ZeroMQ、Kafka、MetaMQ和RocketMQ等。削峰不是一次性可以解決的方案,而是要層層削峰,每一層都把壓力降到最小,再傳輸給下一層,這樣才能接受更大的壓力與并發。
? 異步處理:秒殺系統是一個高并發系統,采用異步處理模式可以極大地提高系統并發量,其實異步處理就是削峰的一種實現方式。異步處理的設計不僅可以削峰,還可以減輕對緩存、數據庫和I/O的壓力。
? 高可用:所有服務器無論應用層還是數據層都要達到高可用的標準,即任何一臺服務器宕機都可由其他服務器暫時替代,并通過自動或手動的方式迅速重啟服務器,保證用戶幾乎感受不到服務器宕機。
? 緩存:秒殺系統最大的瓶頸一般都是數據庫讀寫。由于數據庫讀寫屬于磁盤I/O,性能很低,如果能夠把部分數據或業務邏輯轉移到內存緩存,則會極大地提升效率。
? 可擴展:這里講的可擴展指一旦性能無法支撐當前并發,則可以迅速通過提升服務器性能或快速平滑地增加集群服務器的方式,頂住當前高峰壓力。當壓力下降時,再通過自動或手動的方式,平滑地卸下集群內部的服務器。
- jQuery Mobile Web Development Essentials(Third Edition)
- 架構不再難(全5冊)
- R語言數據可視化之美:專業圖表繪制指南
- JavaScript 網頁編程從入門到精通 (清華社"視頻大講堂"大系·網絡開發視頻大講堂)
- Learning RabbitMQ
- 零基礎學Python數據分析(升級版)
- JavaScript動態網頁開發詳解
- Mastering ServiceNow(Second Edition)
- ANSYS Fluent 二次開發指南
- 基于ARM Cortex-M4F內核的MSP432 MCU開發實踐
- Node.js 12實戰
- Python數據可視化之美:專業圖表繪制指南(全彩)
- Visual Basic 程序設計實踐教程
- Python繪圖指南:分形與數據可視化(全彩)
- Learning Perforce SCM