前 言
經過前面索引和sql的優化後,現在查詢速度快的飛起,然後,我們繼續回歸到了日常需求的開發中。
3個月過後,訂單表的資料已經達到5000萬了,不過sql一次查詢的時間,基本穩定在300ms以下。
但是某個周一,leader剛開完周會就直接來找你了,直接說:“哎呀,周會上DBA找我了,說咱們訂單組的sql偶爾會超過2s,DBA現在要求優化,平均時間要優化到300ms以下,不過,優化前你要先查下,為什麼sql的查詢時間會偶爾突增。”
問題排查
然後我們就接下了這個任務,接着,我們就根據DBA給的慢sql,去查這條sql的相關日志,然後結合着監控,最後發現這條sql平常一直很穩定,但是在高峰期的時候,這條sql偶爾花費的時間會超過2s。
此時,我們又檢視了一下訂單資料庫所在實體機的資源占用情況,發現高峰期時,這台實體機的資源占用非常高,CPU和記憶體占用率都很高,這下基本就确定原因了。
說白了,就是一到高峰期,大量請求跑到MySQL這查詢資料,此時就會有大量請求密集請求資料庫,然後就會導緻資料庫所在機器的CPU和記憶體占用率都飙升,最終就會導緻MySQL查詢效率極速降低。
leader了解情況後說:“其實資料庫查詢慢,不一定就是MySQL資料量大導緻的,比如目前這個情況,明顯是大量請求密集請求資料庫,造成資料庫負載變大,進而大大降低了資料庫的查詢效率,這個時候,其實我們就需要在MySQL的前邊,加上一層緩存,來進行流量削峰,以保證MySQL能穩定的完成查詢”
經過leader一點撥,我們恍然大悟,原來是這樣,說白了,這個時候我們可以加一些緩存,來為MySQL進行流量削峰,添加了緩存後的運作流程,大概是這樣的:
就是說,按照标準的請求流程,使用者的請求是會打到資料庫上的,但是加了緩存之後就不是這種流程了。這個時候請求可以直接從緩存中擷取到資料并傳回,此時就會減少後續流程的處理,比如查詢資料庫的操作,這樣就有效降低了資料庫的負載。
說白了,就是使用緩存來承接大多數的查詢請求,達到流量削峰的效果,進而降低資料庫的負載,以保證MySQL能穩定高效的完成查詢,這樣MySQL在高峰期查詢時間突增的問題就可以完美解決了。
雖然緩存非常好用,但是使用緩存的過程中,我們要關注緩沖的命中率,命中率=傳回正确結果數/請求緩存次數,命中率是衡量緩存有效性的重要名額,命中率越高,說明緩存的使用率越高。
除了要關注緩存命中率,我們還要了解緩存的清空政策,比如 先進先出政策FIFO(first in first out)、最少使用政策LFU(less frequently used) 和最近最少使用政策LRU(least recently used)。
如何提高緩存命中率
剛才我們也說了,命中率是衡量緩存有效性的重要名額,那麼怎麼才能提高緩存命中率呢?
其實要想提高緩存命中率,需要考慮的點有很多,大概有以下幾點:
1.選擇合适的業務場景
首先,緩存适合讀多寫少的場景,最好還是高頻通路的場景,因為通路頻率越高,命中率也就越高。
2.合理設定緩存容量
緩存容量如果太小的話,會觸發Redis的記憶體淘汰機制,這樣就會導緻一些緩存key被删除,就會降低緩存命中率,是以,合理設定緩存容量是非常有必要的。
3.控制好緩存粒度
緩存的粒度越小,緩存命中率越高,因為單個key的資料機關越小的話,這個緩存就越不容易發生更改。
4.靈活設定緩存key的過期時間
這裡說的是,要盡量避免緩存同時過期,如果緩存同時過期的話,假如此時有多個查詢請求,那麼這些請求就都會打到資料庫上去。這種情況叫做緩存擊穿,這會導緻資料庫的壓力很大。
5.避免緩存穿透
先來了解下緩存命中率,比如當請求過來查詢一條資料時,如果在緩存中沒有查到這條資料,此時,我們可以說沒有命中緩存,如果大量查詢請求在緩存中都很少能查到資料,我們就可以說緩存命中率很低。
當緩存命中率很低時,因為在緩存中查不到資料,這個時候請求就會打到資料中,去資料庫中查詢資料,如果資料庫中依然沒有查到資料,說明這個請求已經穿透緩存了。
一旦緩存穿透了,當海量的請求湧來時,如果一直命中不了緩存,海量的請求就會轉而湧向資料庫,而資料庫處理請求的能力是有限的,此時資料庫可能因為請求量暴增壓力過大而當機,資料庫一旦當機,就很有可能演化成緩存雪崩,導緻整個系統大面積的陷入癱瘓,這是非常恐怖的。
是以,我們需要提前做好兜底方案,以此來避免緩存穿透的發生,比如當一個查詢請求過來時,如果緩存中沒有查詢到資料,資料庫中也還是沒有查詢到資料,此時,我們可以在緩存中,給這個查詢請求設定一個空對象,然後請求拿着這個空對象傳回。
同樣的查詢請求下一次再過來時,直接就可以在緩存中命中這個空對象了,請求就不需要湧向資料庫了,這樣就算海量請求湧來時,也可以做到緩存命中率很高,緩存穿透的問題也就解決了。
6.做好緩存預熱
一般來說,第一次查詢的請求都會打到資料庫上去,是以,我們可以提前将資料庫的資料加載到緩存中,也就是緩存預熱,這樣的話第一次查詢請求也可以直接走緩存了。
以上幾點都做好的話,那麼緩存命中率自然就提高了,好了,接下來廢話也不多說了,我們一起來搞一把緩存實戰,來切身感受下加了緩存後的查詢效果。
緩存實戰
場景介紹:曆史訂單查詢
由于已完成的訂單狀态不會再發生變化,是以再進行曆史訂單查詢時會将查詢結果緩存進redis,并設定失效時間為一小時,是以在緩存失效前,使用者再次查詢曆史訂單時則會直接請求redis,減小資料庫壓力
未添加緩存的查詢時間
Redis優化思路
查詢曆史訂單時會先查詢redis中是否有緩存,如有則直接傳回redis中資料,如無則會查詢MySQL,然後将查詢資料傳回,同時将查詢結果設定到緩存中,以便下一次查詢可以走緩存。
緩存Key的生成規則
使用者id+頁碼+頁數生成redis Key
緩存核心代碼
緩存優化後的效果
加緩存後可以看到第二次請求時走了redis緩存查詢,效率有了極大的提升。
然後,你加了緩存之後,發現效果确實不錯,大量請求打到了緩存上,資料庫的資源占用率也維持在一個合理的範圍,sql查詢時間也都穩定在了300ms以下。
------------- END -------------
另外推薦儒猿課堂的1元系列課程給您,歡迎加入一起學習~
網際網路Java工程師面試突擊課(1元專享):
https://tod.h5.xeknow.com/s/3o8O0w
SpringCloudAlibaba零基礎入門到項目實戰(1元專享):
https://tod.h5.xeknow.com/s/1NRrqb
億級流量下的電商詳情頁系統實戰項目(1元專享):
https://tod.h5.xeknow.com/s/xItYJ
Kafka消息中間件核心源碼精講(1元專享):
https://tod.h5.xeknow.com/s/1T0B9m
12個實戰案例帶你玩轉Java并發程式設計(1元專享):
https://tod.h5.xeknow.com/s/2nTSHI
Elasticsearch零基礎入門到精通(1元專享):
https://tod.h5.xeknow.com/s/40xOVv
基于Java手寫分布式中間件系統實戰(1元專享):
https://tod.h5.xeknow.com/s/1MUj1A
基于ShardingSphere的分庫分表實戰課(1元專享):
https://tod.h5.xeknow.com/s/3o6jC