筆記速覽 使用快取遇到的問題及解決方式

2021-09-26 05:05:51 字數 2990 閱讀 6861

在專案中使用快取的案例

關聯維表 :本地快取,自定義map的軟/弱引用

flink + kafka 實現 exactly-once 語義 : 分布式快取,基於redis實現

當某一時刻發生大規模的快取失效的情況,比如快取服務宕機,會有大量的請求進來直接打到db上面,db 撐不住,掛掉。

解決方式

(事前)使用集群快取,保證快取服務的高可用

如使用 redis,可以使用 主從+哨兵 ,redis cluster 來避免 redis 全盤崩潰

(事中)ehcache本地快取 + hystrix限流&降級,避免mysql***

(事後)開啟redis持久化機制,盡快恢復快取集群

查詢不存在資料

解決方式

快取空值,為這些key對應的值設定為null 丟到快取裡面去,後面再出現查詢這個key的請求的時候,直接返回null 。

bloomfilter

大量的請求同時查詢乙個 key 時,此時這個key正好失效了,就會導致大量的請求都打到資料庫上面去

解決方式

多個執行緒同時去查詢資料庫的這條資料,那麼我們可以在第乙個查詢資料的請求上使用乙個 互斥鎖來鎖住它。

等第乙個執行緒查詢到了資料,然後做快取。後面的執行緒進來發現已經有快取了,就直接走快取。

熱點資料快取失效以後,會存在大量的請求過來,然後打到資料庫

解決方式:

設定不同的失效時間,比如在乙個基礎的時間上加上或者減去乙個範圍內的隨機值。

互斥鎖(不建議)

cache aside pattern

(1)讀的時候,先讀快取,快取沒有的話,那麼就讀資料庫,然後取出資料後放入快取,同時返回響應

(2)更新的時候,先刪除快取,然後再更新資料庫

為什麼是刪除快取,而不是更新快取呢?

(1) 複雜點的快取的場景,因為快取有的時候,不簡單是資料庫中直接取出來的值,而是快取計算後的值。

(2)lazy計算的思想:不要每次都重新做複雜的計算,不管它會不會用到,而是讓它到需要被使用的時候再重新計算(28法則)

情況一:先修改資料庫,再刪除快取,但是刪除快取失敗了

問題

導致資料庫中是新資料,快取中是舊資料,資料出現不一致

解決思路

先刪除快取,再修改資料庫

如果刪除快取成功了,修改資料庫失敗了,那麼資料庫中是舊資料,快取中是空的,那麼資料不會不一致

因為讀的時候快取沒有,則讀資料庫中舊資料,然後更新到快取中

情況二:

資料發生了變更,先刪除了快取,然後要去修改資料庫,此時還沒修改

乙個請求過來,去讀快取,發現快取空了,去查詢資料庫,查到了修改前的舊資料,放到了快取中

資料變更的程式完成了資料庫的修改

此時,資料庫和快取中的資料不一樣了。。。。

只有在對乙個資料在併發的進行讀寫的時候,才可能會出現這種問題

其實如果說你的併發量很低的話,特別是讀併發很低,每天訪問量就1萬次,那麼很少的情況下,會出現剛才描述的那種不一致的場景

但是問題是,如果每天的是上億的流量,每秒併發讀是幾萬,每秒只要有資料更新的請求,就可能會出現上述的資料庫+快取不一致的情況

高併發了以後,問題是很多的

更新資料的時候,根據資料的唯一標識,將操作路由之後,傳送到乙個jvm內部的佇列中

如相同的商品id,進行hash取模,再加上對記憶體佇列的數量進行取模,則每個商品都可以路由到某乙個記憶體佇列中

讀取資料的時候,如果發現資料不在快取中,那麼將重新讀取資料+更新快取的操作,根據唯一標識路由之後,也傳送同乙個jvm內部的佇列中

乙個佇列對應乙個工作執行緒

每個工作執行緒序列拿到對應的操作,然後一條一條的執行

這樣的話,乙個資料變更的操作,先執行,刪除快取,然後再去更新資料庫,但是還沒完成更新

此時如果乙個讀請求過來,讀到了空的快取,那麼可以先將快取更新的請求傳送到佇列中,此時會在佇列中積壓,然後同步等待快取更新完成

這裡有乙個優化點,乙個佇列中,其實多個更新快取請求串在一起是沒意義的,因此可以做過濾,如果發現佇列中已經有乙個更新快取的請求了,那麼就不用再放個更新請求操作進去了,直接等待前面的更新操作請求完成即可

如果請求還在等待時間範圍內,不斷輪詢發現可以取到值了,那麼就直接返回; 如果請求等待的時間超過一定時長,那麼這一次直接從資料庫中讀取當前的舊值

讀請求長時阻塞

由於讀請求進行了非常輕度的非同步化,所以一定要注意讀超時的問題,每個讀請求必須在超時時間範圍內返回

該解決方案,最大的風險點在於說,可能資料更新很頻繁,導致佇列中積壓了大量更新操作在裡面,然後讀請求會發生大量的超時,最後導致大量的請求直接走資料庫

務必通過一些模擬真實的測試,看看更新資料的頻繁是怎樣的

另外一點,因為乙個佇列中,可能會積壓針對多個資料項的更新操作,因此需要根據自己的業務情況進行測試,可能需要部署多個服務,每個服務分攤一些資料的更新操作

讀請求併發量過高

這裡還必須做好壓力測試,確保恰巧碰上上述情況的時候,還有乙個風險,就是突然間大量讀請求會在幾十毫秒的延時hang在服務上,看服務能不能抗的住,需要多少機器才能抗住最大的極限情況的峰值

但是因為並不是所有的資料都在同一時間更新,快取也不會同一時間失效,所以每次可能也就是少數資料的快取失效了,然後那些資料對應的讀請求過來,併發量應該也不會特別大

多服務例項部署的請求路由

可能這個服務部署了多個例項,那麼必須保證,執行資料更新操作,以及執行快取更新操作的請求,都通過nginx伺服器路由到相同的服務例項上

熱點商品的路由問題,導致請求的傾斜

萬一某個商品的讀寫請求特別高,全部打到相同的機器的相同的佇列裡面去了,可能造成某台機器的壓力過大

就是說,因為只有在商品資料更新的時候才會清空快取,然後才會導致讀寫併發,所以更新頻率不是太高的話,這個問題的影響並不是特別大

但是的確可能某些機器的負載會高一些

分布式鎖 + 時間版本

參考:中華石衫

筆記速覽 訊息佇列的問題

總結自 中華石衫 如何保證訊息佇列的高可用啊?缺點 導致系統可用性降低 就kafka來說 ha機制,就是replica副本機制 isr leader 掛了咋辦?broker掛了咋辦?controller掛了咋辦?訊息佇列重複消費?如何保證訊息不被重複消費啊 如何保證訊息消費時的冪等性 原因 任務重啟...

使用pageHelper所遇到的問題及解決方法

問題2問題3 jsp頁面分頁 第一次使用該外掛程式確實帶來了許多問題 配置pagehelper時,沒有注意版本問題,pagehelper4.x.x.jar和pagehelper5.x.x.jar 配置檔案是由區別的 pagehelper4.x.x.jar 配置 factory class org.m...

lunix 下編譯draco遇到的問題及解決辦法

1.在windows7下編譯檔案,失敗 2.新建乙個build資料夾,cd到此目錄下,執行命令 cmake path to draco 這是draco資料夾的目錄 這樣在build目錄下 並不會生成可執行檔案,而是生成可以用make編譯的檔案,以及makefile.3.在build資料夾下,直接執行...