redis是一個內存數據庫,使用key-value形式在內存中管理數據。
一、redis使用場景
1、熱數據存儲。對於需要頻繁讀寫的數據,可以放到redis中,不用頻繁的請求數據庫。再設置策略持久化到數據庫中。
2、token管理。用戶登錄成功后,生成token,存儲到redis中,並設置過期時間,可以模擬session,用於做認證中心。
3、緩解數據庫壓力。大量並發請求訪問數據庫會造成數據庫崩潰,可以使用redis做緩沖,先訪問redis。
二、redis為啥這么塊
主要有3個原因:
- 純內存操作。所有的操作都是在內存中進行。
- 單線程操作。單線程操作避免了頻繁的上下文切換。
- 使用非阻塞I/O多路復用機制。客戶端會產生具有不同事件類型的socket。在服務端,有一段I/0多路復用程序,將socket置入隊列之中。然后,文件事件分派器,依次去隊列中取,轉發到不同的事件處理器中。
三、redis數據結構
- string 就是常規的set/get操作
- list 隊列
- set 不重復的集合
- sorted set 有序的不重復集合
- hash 結構化的對象。
四、redis持久化
在因為故障等原因導致服務停止時,內存的數據就會丟失。因此,需要將內存的數據持久化到硬盤上。
redis持久化有兩種方式:RDB、AOF
1、RDB方式持久化
是通過快照來完成的。當滿足一定條件時,redis會將內存中的數據進行快照並存儲到硬盤上。
進行快照的條件在配置文件中配置,由兩個參數構成:時間、改動鍵的個數。當在指定時間內,被改動鍵的個數大於指定個數時,則觸發RDB持久化。
配置文件中默認有3個條件,這些條件是或的關系。
1 save 900 1 # 15分鍾內至少有一個鍵被更改 2 save 300 10 # 5分鍾內至少有10個鍵被更改 3 save 60 10000 # 1分鍾內至少有10000個鍵被更改
默認的rdb文件在當前的目錄,文件名是dump.rdb。
可以對Redis執行手動快照操作,命令是:save、bgsave。兩者有區別:save是由主進程進行快照,會阻塞其他請求。bgsave通過fork子進程進行快照。
注意:由於Redis使用fork來復制一份當前進程,那么子進程就會占有和主進程一樣的內存資源,比如說主進程8G內存,那么在備份的時候,必須保證有16G的內存,要不然會啟用虛擬內存,性能非常的差。
2、AOF方式持久化
是將發送到Redis服務端的每一條命令都記錄下來,並且保存在硬盤的AOF文件中。
可以通過參數appendonly來設置是否啟用AOF。AOF文件的位置和RDB的位置相同,都是通過dir參數設置,默認的文件名是appendonly.aof,可以通過appendfilename參數修改。
五、redis過期刪除策略以及內存淘汰機制
過期刪除策略有:定時刪除、惰性刪除。
定時刪除:有一個定時器在定時隨機掃描一些key,key過期了則刪除。這種策略因為定時器一直在掃描,很消耗系統資源。同時有一些key到了過期時間也沒有刪除。
惰性刪除:在使用key的時候,再檢查是否過期,過期了就刪除。
定時刪除和惰性刪除可以配合使用。
有個問題是:如果有些key沒有被定時刪除任務掃描到,同時又長時間沒有被調用到,那么這些key是不會被刪除的。長期這樣會占用大量內存。
這就需要使用到內存淘汰機制。
內存淘汰機制在配置文件中開啟和配置:
在redis.conf中有一行配置
# maxmemory-policy volatile-lru
有6種內存淘汰機制,一般使用的是:allkeys-lru。當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key。
六、問題
1、redis和數據庫一致性問題
數據庫和緩存雙寫,必然存在數據不一致問題,對數據有強一致性要求的不能將數據放到緩存中。
2、緩存穿透。
大量的請求沒有請求緩存,而是都請求數據庫了。一般發生在黑客故意去請求緩存中不存在的數據,導致所有的請求都懟到數據庫上,從而數據庫連接異常。
解決方案:
(一)利用互斥鎖。當請求從緩存中得不到數據時,先去獲得鎖,得到鎖了,再去請求數據庫。沒得到鎖,則休眠一段時間重試,這樣能避免大量的請求數據庫。
(二)采用異步更新策略。無論請求是否取到值,都直接返回。value值中維護一個緩存失效時間,緩存如果過期,異步起一個線程去讀數據庫,更新緩存。需要做緩存預熱(項目啟動前,先加載緩存)操作。
(三)提供一個能迅速判斷請求是否有效的攔截機制。比如,利用布隆過濾器,內部維護一系列合法有效的key。迅速判斷出請求所攜帶的Key是否合法有效。如果不合法,則直接返回。
3、緩存雪崩
緩存在同一時間大面積失效,這時候又有大量的請求,則這些請求會請求到數據庫,導致數據庫異常。
解決方案:
(一)給緩存的失效時間加上一個隨機值,避免集體失效。
(二)使用互斥鎖,避免大量數據庫請求。
(三)雙緩存。我們有兩個緩存,緩存A和緩存B。緩存A的失效時間為20分鍾,緩存B不設失效時間。自己做緩存預熱操作。然后細分以下幾個小點:
- 從緩存A讀數據庫,有則直接返回。
- A沒有數據,直接從B讀數據,直接返回,並且異步啟動一個更新線程。
- 更新線程同時更新緩存A和緩存B。
4、redis並發競爭key
多個子系統去set一個value。
解決方案:
1、設置分布式鎖,搶到鎖的就去set。
2、設置隊列,根據實際需求將set操作變成串行。