多級緩存
傳統緩存: 傳統緩存策略一般是請求到達Tomcat后,先查詢Redis,如果未命中則查詢數據庫。
這種模式下請求一定要經過Tomcat處理,Tomcat的性能就成為了整個系統的瓶頸;并且Redis的緩存也有過期時間,一旦過期,請求依舊會直接打到數據庫,對數據庫帶來沖擊。
多級緩存: 多級緩存就是充分利用請求處理的每個環節,分別添加上緩存,減輕tomcat壓力,提升服務器性能。例如:瀏覽器客戶端緩存 、 Nginx本地緩存 、 Redis緩存 、 Tomcat進程緩存 。(緩存從左到右依次向下,最后到達數據庫,注意這里的Redis緩存在Tomcat前面了,因為Nginx里也可以編程)
垂直分表
同一個商品的信息可以考慮保存在兩張表里面,關于這個商品的不同類型的信息各自保存在一個表里面。例如可以是商品庫存和售出信息保存在一張表,商品的基本信息保存在另外一張表里面,這樣當商品庫存發生變化的時候,緩存中的商品基本信息可以不用改動。
Nginx
nginx是 反向代理服務器(接收客戶端請求,轉發給后端服務) , 負載均衡器(多個后端服務之間分發請求,提高系統吞吐量) , 靜態資源服務器(提供HTML、CSS、JS、圖片等靜態資源) 等。 用戶的請求會先來到Nginx,靜態資源在nginx中獲取,頁面上的數據通過ajax異步地從后端服務中獲取。
Caffeine
分布式緩存: 例如Redis,可以在集群中 共享 ,容量更大,可靠性更高。但是會有網絡IO地開銷。
進程本地緩存: 例如HashMap、GuavaCache等,緩存在本地,沒有網絡IO開銷,速度更快。但是存儲容量有限,且集群間無法共享。
Caffeine是目前性能最高,更強大的緩存庫。它可以設置緩存的 容量 、過期策略(按時間過期、按容量過期) 、超強的并發性能 。
Lua腳本
由于我們將redis層放在的tomcat進程層的上一層,所以操作redis的事情就由nginx來干了,而Lua是操作nginx的語言。
lua是一種輕量小巧的腳本語言,用C語言編寫,設計目的是嵌入應用程序中。
OpenResty
OpenResty是一個基于Nginx的高性能Web平臺,通過Lua擴展Nginx實現的可伸縮的Web平臺。OpenResty內置了Nginx,在nginx的基礎上進行了功能的擴展,可以直接把這個當成nginx來使用,只是可以有更多的功能。
本來nginx是攔截請求之后轉發給后端的tomcat服務器來處理,現在是交給lua腳本來處理,在lua腳本中填寫業務邏輯。
原生的Nginx不支持lua模塊,我們就沒辦法再nginx中做業務處理。有了OpenResty之后,我們就可以在Nginx中引入lua腳本模塊,然后通過編寫lua腳本在nginx做業務邏輯的處理,可以在nginx層就做一層緩存,瀏覽器請求先被nginx捕捉到然后執行lua腳本,腳本中封裝了http請求,再將該請求發送出去由nginx轉發給后續業務(redis層或者tomcat層)。
Redis緩存預熱
服務剛剛啟動時,redis中并沒有緩存,如果所有商品數據都在第一次查詢的時候添加緩存,那么數據庫就會面臨很大的壓力。所以就要在實際開發中做緩存的預熱,利用大數據統計用戶訪問的熱點數據,在項目啟動的時候將這些熱點數據提前放到緩存中。
通過配置一個RedisHandler的Bean實現Initializing接口并且實現afterPropertieSet()方法,在其中實現緩存預熱。這里涉及到Bean的生命周期,這個afterPropertieSet方法是在Bean的依賴注入之后自動調用。
Nginx層查詢Redis
OpenResty也為Nginx提供了操作Redis的模塊,只要引入該模塊就能直接使用,直接操作Redis了。這里和redis建立連接使用的是長連接,使用完后會放進連接池,規定時間沒用該連接才會被釋放。
Nginx本地緩存
OpenResty為Nginx提供了shard dict功能,可以在nginx的多個worker之間共享數據,實現nginx本地緩存功能。
修改item.lua中的read_data函數,優先查詢本地緩存,未命中的時候再查詢Redis、Tomcat,查詢Redis和Tomcat成功后,將數據寫入本地緩存,并設置有效期。
數據同步
數據庫(真實數據)與緩存數據的一致性是我們要解決的問題,也就是數據同步問題。
常見的數據同步方法有3種:
設置有效期: 緩存設置有效期,到期自動刪除,再次查詢的時候更新。
同步雙寫: 更新數據庫的時候,直接修改緩存。
異步通知: 更新數據庫的時候發送事件通知,使用MQ或者其它機制(監聽數據庫binlog)來接收通知后修改緩存數據。
Canal監聽binlog
camal是通過mysql的主從同步使用的binlog來實現了,canal把自己偽裝成了一個slave節點,然后需要監聽的mysql作為master生成binlog,canal不斷去讀取master的binlog,如果binlog發生變化了,那么canal立刻就知道了,然后根據變化來進行后續的處理。所以要想使用canal,還要去實現mysql的 主從同步 。