緩存
-
緩存的作用:
高併發、高性能
高性能:查詢速度快
高併發:緩存是走內存的,內存天然就支撐高併發
-
常見緩存問題:
- 緩存與數據庫雙寫不一致
- 緩存雪崩、緩存穿透、緩存擊穿
- 緩存併發競爭
-
如何保證緩存和數據庫一致性:
使用串行化,即:將讀請求和寫請求放到一個內存隊列中。
串行化可以保證緩存與數據庫一直,但是會導致系統吞吐量大幅度降低。非嚴格要求,不要串行化。
經典緩存+DB讀寫模式
》讀:先讀緩存,沒有讀取DB,讀出來放入緩存。
》更新:先更新DB,再刪除緩存。
-
緩存雪崩
請求量大時,緩存此時也跌機了,導致大量請求直接請求DB,帶着DB也跌機了。
緩存雪崩的事前事中事後的解決方案:
- 事前:Redis 高可用,主從+哨兵,Redis cluster,避免全盤崩潰。
- 事中:本地 ehcache 緩存 + hystrix 限流&降級,避免 MySQL 被打死。
- 事後:Redis 持久化,一旦重啓,自動從磁盤上加載數據,快速恢復緩存數據。
-
緩存穿透
緩存中查詢不到,大量的請求直接查詢DB.
解決方案:
- 每次系統 A 從數據庫中只要沒查到,就寫一個空值到緩存裏去,比如
set -999 UNKNOWN
。然後設置一個過期時間,這樣的話,下次有相同的 key 來訪問的時候,在緩存失效之前,都可以直接從緩存中取數據。
- 每次系統 A 從數據庫中只要沒查到,就寫一個空值到緩存裏去,比如
-
緩存擊穿
某個 key 非常熱點,訪問非常頻繁,處於集中式高併發訪問的情況,當這個 key 在失效的瞬間,大量的請求就擊穿了緩存,直接請求數據庫,就像是在一道屏障上鑿開了一個洞。
解決方案:
- 若緩存的數據是基本不會發生更新的,則可嘗試將該熱點數據設置爲永不過期。
- 若緩存的數據更新不頻繁,且緩存刷新的整個流程耗時較少的情況下,則可以採用基於 Redis、zookeeper 等分佈式中間件的分佈式互斥鎖,或者本地互斥鎖以保證僅少量的請求能請求數據庫並重新構建緩存,其餘線程則在鎖釋放後能訪問到新緩存。
- 若緩存的數據更新頻繁或者在緩存刷新的流程耗時較長的情況下,可以利用定時線程在緩存過期前主動地重新構建緩存或者延後緩存的過期時間,以保證所有的請求能一直訪問到對應的緩存。
-
緩存不一致解決方案
-
先刪除緩存,再更新數據庫。如果數據庫更新失敗了,那麼數據庫中是舊數據,緩存中是空的,那麼數據不會不一致。因爲讀的時候緩存沒有,所以去讀了數據庫中的舊數據,然後更新到緩存中。
-
億流量級高併發場景下緩存不一致問題:
數據發生了變更,先刪除了緩存,然後要去修改數據庫,此時還沒修改。一個請求過來,去讀緩存,發現緩存空了,去查詢數據庫,查到了修改前的舊數據,放到了緩存中。隨後數據變更的程序完成了數據庫的修改。完了,數據庫和緩存中的數據不一樣了..
解決方案:入隊列(jvm內存隊列) > (阻塞 超時即新引入的問題)
-
-
完續......