redis學習筆記
redis筆記
1. Redis是什么?
Redis( Remote Dictionary Server )是一個使用 C 語言編寫的,高性能非關系型的鍵值對數據庫。
與傳統數據庫不同的是,Redis 的數據是存在內存中的,所以讀寫速度非常快,被廣泛應用于緩存方
向。Redis可以將數據寫入磁盤中,保證了數據的安全不丟失,而且Redis的操作是原子性的。
2. Redis優缺點?
優點:
-
基于內存操作,內存讀寫速度快。
-
Redis是單線程的,避免線程切換開銷及多線程的競爭問題。單線程是指網絡請求使用一個線程來
處理,即一個線程處理所有網絡請求,Redis 運行時不止有一個線程,比如數據持久化的過程會另
起線程。
-
支持多種數據類型,包括String、Hash、List、Set、ZSet等。
-
支持持久化。Redis支持RDB和AOF兩種持久化機制,持久化功能可以有效地避免數據丟失問題。
-
支持事務。Redis的所有操作都是原子性的,同時Redis還支持對幾個操作合并后的原子性執行。
-
支持主從復制。主節點會自動將數據同步到從節點,可以進行讀寫分離。
缺點:
-
對結構化查詢的支持比較差。
-
數據庫容量受到物理內存的限制,不適合用作海量數據的高性能讀寫,因此Redis適合的場景主要
局限在較小數據量的操作。
- Redis 較難支持在線擴容,在集群容量達到上限時在線擴容會變得很復雜。
3. Redis為什么這么快?
基于內存:Redis是使用內存存儲,沒有磁盤IO上的開銷。數據存在內存中,讀寫速度快。
單線程實現( Redis 6.0以前):Redis使用單個線程處理請求,避免了多個線程之間線程切換和鎖
資源爭用的開銷。
IO多路復用模型:Redis 采用 IO 多路復用技術。Redis 使用單線程來輪詢描述符,將數據庫的操作
都轉換成了事件,不在網絡I/O上浪費過多的時間。
高效的數據結構:Redis 每種數據類型底層都做了優化,目的就是為了追求更快的速度。
4. Redis為何選擇單線程
避免過多的上下文切換開銷。程序始終運行在進程中單個線程內,沒有多線程切換的場景。
避免同步機制的開銷:如果 Redis選擇多線程模型,需要考慮數據同步的問題,則必然會引入某些
同步機制,會導致在操作數據過程中帶來更多的開銷,增加程序復雜度的同時還會降低性能。
實現簡單,方便維護:如果 Redis使用多線程模式,那么所有的底層數據結構的設計都必須考慮線
程安全問題,那么 Redis 的實現將會變得更加復雜。
5. Redis6.0為何引入多線程?
Redis支持多線程主要有兩個原因:
可以充分利用服務器 CPU 資源,單線程模型的主線程只能利用一個cpu;
多線程任務可以分攤 Redis 同步 IO 讀寫的負荷。
6. Redis應用場景有哪些?
-
緩存熱點數據,緩解數據庫的壓力。
-
利用 Redis 原子性的自增操作,可以實現計數器的功能,比如統計用戶點贊數、用戶訪問數等。
-
簡單的消息隊列,可以使用Redis自身的發布/訂閱模式或者List來實現簡單的消息隊列,實現異步
操作。
- 限速器,可用于限制某個用戶訪問某個接口的頻率,比如秒殺場景用于防止用戶快速點擊帶來不必
要的壓力。
- 好友關系,利用集合的一些命令,比如交集、并集、差集等,實現共同好友、共同愛好之類的功
能。
7. Memcached和Redis的區別?
-
Redis 只使用單核,而 Memcached 可以使用多核。
-
MemCached 數據結構單一,僅用來緩存數據,而 Redis 支持多種數據類型。
-
MemCached 不支持數據持久化,重啟后數據會消失。Redis 支持數據持久化。
-
Redis 提供主從同步機制和 cluster 集群部署能力,能夠提供高可用服務。Memcached 沒有提供
原生的集群模式,需要依靠客戶端實現往集群中分片寫入數據。
-
Redis 的速度比 Memcached 快很多。
-
Redis 使用單線程的多路 IO 復用模型,Memcached使用多線程的非阻塞 IO 模型。
8. Redis 數據類型有哪些?
基本數據類型:
1、String:最常用的一種數據類型,String類型的值可以是字符串、數字或者二進制,但值最大不能超
過512MB。
2、Hash:Hash 是一個鍵值對集合。
3、Set:無序去重的集合。Set 提供了交集、并集等方法,對于實現共同好友、共同關注等功能特別方
便。
4、List:有序可重復的集合,底層是依賴雙向鏈表實現的。
5、SortedSet:有序Set。內部維護了一個 score 的參數來實現。適用于排行榜和帶權重的消息隊列等
場景。
特殊的數據類型:
1、Bitmap:位圖,可以認為是一個以位為單位數組,數組中的每個單元只能存0或者1,數組的下標在
Bitmap 中叫做偏移量。Bitmap的長度與集合中元素個數無關,而是與基數的上限有關。
2、Hyperloglog。HyperLogLog 是用來做基數統計的算法,其優點是,在輸入元素的數量或者體積非
常非常大時,計算基數所需的空間總是固定的、并且是很小的。典型的使用場景是統計獨立訪客。
3、Geospatial :主要用于存儲地理位置信息,并對存儲的信息進行操作,適用場景如定位、附近的人
等。
9. keys命令存在的問題?
redis的單線程的。keys指令會導致線程阻塞一段時間,直到執行完畢,服務才能恢復。scan采用漸進式
遍歷的方式來解決keys命令可能帶來的阻塞問題,每次scan命令的時間復雜度是 O(1) ,但是要真正實
現keys的功能,需要執行多次scan。
scan的缺點:在scan的過程中如果有鍵的變化(增加、刪除、修改),遍歷過程可能會有以下問題:新
增的鍵可能沒有遍歷到,遍歷出了重復的鍵等情況,也就是說scan并不能保證完整的遍歷出來所有的
鍵。
10.SortedSet和List異同點?
相同點:
-
都是有序的;
-
都可以獲得某個范圍內的元素。
不同點:
-
列表基于鏈表實現,獲取兩端元素速度快,訪問中間元素速度慢;
-
有序集合基于散列表和跳躍表實現,訪問中間元素時間復雜度是OlogN;
-
列表不能簡單的調整某個元素的位置,有序列表可以(更改元素的分數);
-
有序集合更耗內存。
11. Redis事務
事務的原理是將一個事務范圍內的若干命令發送給Redis,然后再讓Redis依次執行這些命令。
事務的生命周期:
-
使用MULTI開啟一個事務
-
在開啟事務的時候,每次操作的命令將會被插入到一個隊列中,同時這個命令并不會被真的執行
-
EXEC命令進行提交事務
-
discard放棄執行事務
一個事務范圍內某個命令出錯不會影響其他命令的執行,不保證原子性:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 1 2
QUEUED
127.0.0.1:6379> set c 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR syntax error
3) OK
WATCH命令
WATCH 命令可以監控一個或多個鍵,一旦其中有一個鍵被修改,之后的事務就不會執行(類似于樂觀
鎖)。執行 EXEC 命令之后,就會自動取消監控。
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> set name 1
OK
127.0.0.1:6379> multi比如上面的代碼中:
\1. watch name 開啟了對 name 這個 key 的監控
\2. 修改 name 的值
\3. 開啟事務a
\4. 在事務a中設置了 name 和 gender 的值
\5. 使用 EXEC 命令進提交事務
\6. 使用命令 get gender 發現不存在,即事務a沒有執行
使用 UNWATCH 可以取消 WATCH 命令對 key 的監控,所有監控鎖將會被取消。
事務沖突:


12. 持久化機制
持久化就是把內存的數據寫到磁盤中,防止服務宕機導致內存數據丟失。
Redis支持兩種方式的持久化,一種是 RDB 的方式,一種是 AOF 的方式。前者會根據指定的規則定時將
內存中的數據存儲在硬盤上,而后者在每次執行完命令后將命令記錄下來。一般將兩者結合使用。
12.1. RDB****方式
RDB 是 Redis 默認的持久化方案。RDB持久化時會將內存中的數據寫入到磁盤中,在指定目錄下生成一
個 dump.rdb 文件。Redis 重啟會加載 dump.rdb 文件恢復數據。
bgsave 是主流的觸發 RDB 持久化的方式,執行過程如下:
127.0.0.1:6379> set name 2
QUEUED
127.0.0.1:6379> set gender 1
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get gender
(nil)
127.0.0.1:6379> multi比如上面的代碼中:
-
watch name 開啟了對 name 這個 key 的監控
-
修改 name 的值
-
開啟事務a
-
在事務a中設置了 name 和 gender 的值
-
使用 EXEC 命令進提交事務
-
使用命令 get gender 發現不存在,即事務a沒有執行
使用 UNWATCH 可以取消 WATCH 命令對 key 的監控,所有監控鎖將會被取消。
12. 持久化機制
持久化就是把內存的數據寫到磁盤中,防止服務宕機導致內存數據丟失。
Redis支持兩種方式的持久化,一種是 RDB 的方式,一種是 AOF 的方式。前者會根據指定的規則定時將
內存中的數據存儲在硬盤上,而后者在每次執行完命令后將命令記錄下來。一般將兩者結合使用。
12.1. RDB方式
RDB 是 Redis 默認的持久化方案。RDB持久化時會將內存中的數據寫入到磁盤中,在指定目錄下生成一
個 dump.rdb 文件。Redis 重啟會加載 dump.rdb 文件恢復數據。
bgsave 是主流的觸發 RDB 持久化的方式,執行過程如下:

執行 BGSAVE 命令
Redis 父進程判斷當前是否存在正在執行的子進程,如果存在, BGSAVE 命令直接返回。
父進程執行 fork 操作創建子進程,fork操作過程中父進程會阻塞。
父進程 fork 完成后,父進程繼續接收并處理客戶端的請求,而子進程開始將內存中的數據寫進硬
盤的臨時文件;
當子進程寫完所有數據后會用該臨時文件替換舊的 RDB 文件。
Redis啟動時會讀取RDB快照文件,將數據從硬盤載入內存。通過 RDB 方式的持久化,一旦Redis異常退
出,就會丟失最近一次持久化以后更改的數據。
觸發 RDB 持久化的方式:
- 手動觸發:用戶執行 SAVE 或 BGSAVE 命令。 SAVE 命令執行快照的過程會阻塞所有客戶端的請求,
應避免在生產環境使用此命令。 BGSAVE 命令可以在后臺異步進行快照操作,快照的同時服務器還
可以繼續響應客戶端的請求,因此需要手動執行快照時推薦使用 BGSAVE 命令。
- 被動觸發:
根據配置規則進行自動快照,如 SAVE 100 10 ,100秒內至少有10個鍵被修改則進行快照。
如果從節點執行全量復制操作,主節點會自動執行 BGSAVE 生成 RDB 文件并發送給從節點。
默認情況下執行 shutdown 命令時,如果沒有開啟 AOF 持久化功能則自動執行·BGSAVE·。
優點:
- Redis 加載 RDB 恢復數據遠遠快于 AOF 的方式。 (恢復數據快)
- 使用單獨子進程來進行持久化,主進程不會進行任何 IO 操作,保證了 Redis 的高性能。(其實就是節省了磁盤空間)
- 適合大規模的數據恢復
- 對數據的完整性和一致性要求不高,適合使用。
缺點:
- RDB方式數據無法做到實時持久化**。因為 BGSAVE 每次運行都要執行 fork 操作創建子進程,屬于
重量級操作,頻繁執行成本比較高。2. RDB 文件使用特定二進制格式保存,Redis 版本升級過程中有多個格式的 RDB 版本,存在老版本
Redis 無法兼容新版 RDB 格式的問題。
12.2. AOF方式
AOF(append only file)持久化:以獨立日志的方式記錄每次寫命令(讀操作不記錄,文件只允許追加,不允許修改),Redis重啟時會重新執行AOF文
件中的命令達到恢復數據的目的。AOF的主要作用是解決了數據持久化的實時性,AOF 是Redis持久化
的主流方式。
默認情況下Redis沒有開啟AOF方式的持久化,可以通過 appendonly 參數啟用: appendonly yes 。開
啟AOF方式持久化后每執行一條寫命令,Redis就會將該命令寫進 aof_buf 緩沖區,AOF緩沖區根據對
應的策略向硬盤做同步操作。
默認情況下系統每30秒會執行一次同步操作。為了防止緩沖區數據丟失,可以在Redis寫入AOF文件后主
動要求系統將緩沖區數據同步到硬盤上。可以通過 appendfsync 參數設置同步的時機。
接下來看一下 AOF 持久化執行流程:
-
所有的寫入命令會追加到 AOP 緩沖區中。
-
AOF 緩沖區根據對應的策略向硬盤同步。
-
隨著 AOF 文件越來越大,需要定期對 AOF 文件進行重寫,達到壓縮文件體積的目的。AOF文件重
寫是把Redis進程內的數據轉化為寫命令同步到新AOF文件的過程。
- 當 Redis 服務器重啟時,可以加載 AOF 文件進行數據恢復。
優點:
- AOF可以更好的保護數據不丟失,可以配置 AOF 每秒執行一次 fsync 操作,如果Redis進程掛掉,
最多丟失1秒的數據。
appendfsync always //每次寫入aof文件都會執行同步,最安全最慢,不建議配置
appendfsync everysec //既保證性能也保證安全,建議配置
appendfsync no //由操作系統決定何時進行同步操作
接下來看一下 AOF 持久化執行流程:

-
所有的寫入命令會追加到 AOP 緩沖區中。
-
AOF 緩沖區根據對應的策略向硬盤同步。
-
隨著 AOF 文件越來越大,需要定期對 AOF 文件進行重寫,達到壓縮文件體積的目的。AOF文件重
寫是把Redis進程內的數據轉化為寫命令同步到新AOF文件的過程。
- 當 Redis 服務器重啟時,可以加載 AOF 文件進行數據恢復。
優點:
- AOF可以更好的保護數據不丟失,可以配置 AOF 每秒執行一次 fsync 操作,如果Redis進程掛掉,
最多丟失1秒的數據。
appendfsync always //每次寫入aof文件都會執行同步,最安全最慢,不建議配置
appendfsync everysec //既保證性能也保證安全,建議配置
appendfsync no //由操作系統決定何時進行同步操作2. AOF以 append-only 的模式寫入,所以沒有磁盤尋址的開銷,寫入性能非常高。
缺點:
-
對于同一份文件AOF文件比RDB數據快照要大。
-
數據恢復比較慢。
13. RDB和AOF如何選擇?
通常來說,應該同時使用兩種持久化方案,以保證數據安全。
如果數據不敏感,且可以從其他地方重新生成,可以關閉持久化。
如果數據比較重要,且能夠承受幾分鐘的數據丟失,比如緩存等,只需要使用RDB即可。
如果是用做內存數據,要使用Redis的持久化,建議是RDB和AOF都開啟。
如果只用AOF,優先使用everysec的配置選擇,因為它在可靠性和性能之間取了一個平衡。
當RDB與AOF兩種方式都開啟時,Redis會優先使用AOF恢復數據,因為AOF保存的文件比RDB文件更完
整。
14. Redis常見的部署方式有哪些?
Redis的幾種常見使用方式包括:
單機版
Redis主從
Redis Sentinel(哨兵)
Redis Cluster
使用場景:
單機版:很少使用。存在的問題:1、內存容量有限 2、處理能力有限 3、無法高可用。
主從模式:master 節點掛掉后,需要手動指定新的 master,可用性不高,基本不用。
哨兵模式:master 節點掛掉后,哨兵進程會主動選舉新的 master,可用性高,但是每個節點存儲的數
據是一樣的,浪費內存空間。數據量不是很多,集群規模不是很大,需要自動容錯容災的時候使用。
Redis cluster:主要是針對海量數據+高并發+高可用的場景,如果是海量數據,如果你的數據量很大,
那么建議就用Redis cluster,所有主節點的容量總和就是Redis cluster可緩存的數據容量。
15. 主從復制
Redis的復制功能是支持多個數據庫之間的數據同步。主數據庫可以進行讀寫操作,當主數據庫的數據發
生變化時會自動將數據同步到從數據庫。從數據庫一般是只讀的,它會接收主數據庫同步過來的數據。
一個主數據庫可以有多個從數據庫,而一個從數據庫只能有一個主數據庫。
1.讀寫分離
2.容災快速恢復:當一個從數據庫掛掉之后可以從另一個從數據庫讀取數據
主從復制的原理?
-
當啟動一個從節點時,它會發送一個 PSYNC 命令給主節點;
-
如果是從節點初次連接到主節點,那么會觸發一次全量復制。此時主節點會啟動一個后臺線程,開
始生成一份 RDB 快照文件;
- 同時還會將從客戶端 client 新收到的所有寫命令緩存在內存中。 RDB 文件生成完畢后, 主節點會
將 RDB 文件發送給從節點,從節點會先將 RDB 文件寫入本地磁盤,然后再從本地磁盤加載到內存
redis-server //啟動Redis實例作為主數據庫
redis-server --port 6380 --slaveof 127.0.0.1 6379 //啟動另一個實例作為從數據庫
slaveof 127.0.0.1 6379
SLAVEOF NO ONE //停止接收其他數據庫的同步并轉化為主數據庫。
中;
-
接著主節點會將內存中緩存的寫命令發送到從節點,從節點同步這些數據;
-
如果從節點跟主節點之間網絡出現故障,連接斷開了,會自動重連,連接之后主節點僅會將部分缺
失的數據同步給從節點。
薪火相傳:

2.反客為主:

16. 哨兵Sentinel
主從復制存在不能自動故障轉移、達不到高可用的問題。哨兵模式解決了這些問題。通過哨兵機制可以
自動切換主從節點。
客戶端連接Redis的時候,先連接哨兵,哨兵會告訴客戶端Redis主節點的地址,然后客戶端連接上Redis
并進行后續的操作。當主節點宕機的時候,哨兵監測到主節點宕機,會重新推選出某個表現良好的從節
點成為新的主節點,然后通過發布訂閱模式通知其他的從服務器,讓它們切換主機.

工作原理
每個 Sentinel 以每秒鐘一次的頻率向它所知道的 Master , Slave 以及其他 Sentinel 實例發送
一個 PING 命令。
如果一個實例距離最后一次有效回復 PING 命令的時間超過指定值, 則這個實例會被 Sentine 標
記為主觀下線。
如果一個 Master 被標記為主觀下線,則正在監視這個 Master 的所有 Sentinel 要以每秒一次的
頻率確認 Master 是否真正進入主觀下線狀態。
當有足夠數量的 Sentinel (大于等于配置文件指定值)在指定的時間范圍內確認 Master 的確進
入了主觀下線狀態, 則 Master 會被標記為客觀下線 。若沒有足夠數量的 Sentinel 同意 Master
已經下線, Master 的客觀下線狀態就會被解除。 若 Master 重新向 Sentinel 的 PING 命令返
回有效回復, Master 的主觀下線狀態就會被移除。
哨兵節點會選舉出哨兵 leader,負責故障轉移的工作。
哨兵 leader 會推選出某個表現良好的從節點成為新的主節點,然后通知其他從節點更新主節點信
息。

17. Redis cluster
哨兵模式解決了主從復制不能自動故障轉移、達不到高可用的問題,但還是存在主節點的寫能力、容量
受限于單機配置的問題。而cluster模式實現了Redis的分布式存儲,每個節點存儲不同的內容,解決主
節點的寫能力、容量受限于單機配置的問題。
Redis cluster集群節點最小配置6個節點以上(
3主3從),其中主節點提供讀寫操作,從節點作為備用
節點,不提供請求,只作為故障轉移使用。
Redis cluster采用虛擬槽分區,所有的鍵根據哈希函數映射到0~16383個整數槽內,每個節點負責維護
一部分槽以及槽所映射的鍵值數據

哈希槽是如何映射到 Redis 實例上的?
-
對鍵值對的 key 使用 crc16 算法計算一個結果
-
將結果對 16384 取余,得到的值表示 key 對應的哈希槽
-
根據該槽信息定位到對應的實例
優點:
無中心架構,支持動態擴容;
數據按照 slot 存儲分布在多個節點,節點間數據共享,可動態調整數據分布;
高可用性。部分節點不可用時,集群仍可用。集群模式能夠實現自動故障轉移(failover),節點
之間通過 gossip 協議交換狀態信息,用投票機制完成 Slave 到 Master 的角色轉換。
缺點:
不支持批量操作(pipeline)。
數據通過異步復制,不保證數據的強一致性。
事務操作支持有限,只支持多 key 在同一節點上的事務操作,當多個 key 分布于不同的節點上時無
法使用事務功能。
key 作為數據分區的最小粒度,不能將一個很大的鍵值對象如 hash 、 list 等映射到不同的節
點。
不支持多數據庫空間,單機下的Redis可以支持到16個數據庫,集群模式下只能使用1個數據庫空
間。
17.1. 哈希分區算法有哪些?
節點取余分區。使用特定的數據,如Redis的鍵或用戶ID,對節點數量N取余:hash(key)%N計算出
哈希值,用來決定數據映射到哪一個節點上。 優點是簡單性。擴容時通常采用翻倍擴容,避免數據映射
全部被打亂導致全量遷移的情況。
一致性哈希分區。為系統中每個節點分配一個token,范圍一般在0~232,這些token構成一個哈希環。
數據讀寫執行節點查找操作時,先根據key計算hash值,然后順時針找到第一個大于等于該哈希值的
token節點。 這種方式相比節點取余最大的好處在于加入和刪除節點只影響哈希環中相鄰的節點,對其
他節點無影響。
虛擬槽分區,所有的鍵根據哈希函數映射到0~16383整數槽內,計算公式:slot=CRC16(key)
&16383。每一個節點負責維護一部分槽以及槽所映射的鍵值數據。Redis Cluser****采用虛擬槽分區算
法。
18. 過期鍵的刪除策略?
1、被動刪除。在訪問key時,如果發現key已經過期,那么會將key刪除。
2、主動刪除。定時清理key,每次清理會依次遍歷所有DB,從db隨機取出20個key,如果過期就刪除,
如果其中有5個key過期,那么就繼續對這個db進行清理,否則開始清理下一個db。
3、內存不夠時清理。Redis有最大內存的限制,通過maxmemory參數可以設置最大內存,當使用的內
存超過了設置的最大內存,就要進行內存釋放, 在進行內存釋放的時候,會按照配置的淘汰策略清理內
存。
19. 內存淘汰策略有哪些?
當Redis的內存超過最大允許的內存之后,Redis 會觸發內存淘汰策略,刪除一些不常用的數據,以保證
Redis服務器正常運行。
Redisv4.0****前提供 6 種數據淘汰策略:
volatile-lru:LRU( Least Recently Used ),最近使用。利用LRU算法移除設置了過期時間的
key
allkeys-lru:當內存不足以容納新寫入數據時,從數據集中移除最近最少使用的key
volatile-ttl:從已設置過期時間的數據集中挑選將要過期的數據淘汰
volatile-random:從已設置過期時間的數據集中任意選擇數據淘汰
allkeys-random:從數據集中任意選擇數據淘汰
no-eviction:禁止刪除數據,當內存不足以容納新寫入數據時,新寫入操作會報錯
Redisv4.0****后增加以下兩種:
volatile-lfu:LFU,Least Frequently Used,最少使用,從已設置過期時間的數據集中挑選最不
經常使用的數據淘汰。
allkeys-lfu:當內存不足以容納新寫入數據時,從數據集中移除最不經常使用的key。
內存淘汰策略可以通過配置文件來修改,相應的配置項是 maxmemory-policy ,默認配置是
noeviction 。


20. 如何保證緩存與數據庫雙寫時的數據一致性?
1、先刪除緩存再更新數據庫
進行更新操作時,先刪除緩存,然后更新數據庫,后續的請求再次讀取時,會從數據庫讀取后再將新數
據更新到緩存。存在的問題:刪除緩存數據之后,更新數據庫完成之前,這個時間段內如果有新的讀請求過來,就會從
數據庫讀取舊數據重新寫到緩存中,再次造成不一致,并且后續讀的都是舊數據。
2、先更新數據庫再刪除緩存
進行更新操作時,先更新MySQL,成功之后,刪除緩存,后續讀取請求時再將新數據回寫緩存。
存在的問題:更新MySQL和刪除緩存這段時間內,請求讀取的還是緩存的舊數據,不過等數據庫更新完
成,就會恢復一致,影響相對比較小。
3、異步更新緩存
數據庫的更新操作完成后不直接操作緩存,而是把這個操作命令封裝成消息扔到消息隊列中,然后由
Redis自己去消費更新數據,消息隊列可以保證數據操作順序一致性,確保緩存系統的數據正常。

21. 緩存穿透
緩存穿透是指查詢一個不存在的數據,由于緩存是不命中時被動寫的,如果從DB查不到數據則不寫入緩
存,這將導致這個不存在的數據每次請求都要到DB去查詢,失去了緩存的意義。在流量大時,可能DB
就掛掉了。
-
緩存空值,不會查數據庫。
-
采用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的 bitmap 中,查詢不存在的數據會被
這個 bitmap 攔截掉,從而避免了對 DB 的查詢壓力。
布隆過濾器的原理:當一個元素被加入集合時,通過K個散列函數將這個元素映射成一個位數組中的K個
點,把它們置為1。查詢時,將元素通過散列函數映射之后會得到k個點,如果這些點有任何一個0,則
被檢元素一定不在,直接返回;如果都是1,則查詢元素很可能存在,就會去查詢Redis和數據庫。


22. 緩存雪崩
緩存雪崩是指在我們設置緩存時采用了相同的過期時間,導致緩存在某一時刻同時失效,請求全部轉發
到DB,DB瞬時壓力過重掛掉。
解決方法:在原有的失效時間基礎上增加一個隨機值,使得過期時間分散一些。

23. 緩存擊穿
緩存擊穿:大量的請求同時查詢一個 key 時,此時這個 key 正好失效了,就會導致大量的請求都落到數
據庫。緩存擊穿是查詢緩存中失效的 key****,而緩存穿透是查詢不存在的 key****。
解決方法:加分布式鎖,第一個請求的線程可以拿到鎖,拿到鎖的線程查詢到了數據之后設置緩存,其
他的線程獲取鎖失敗會等待50ms然后重新到緩存取數據,這樣便可以避免大量的請求落到數據庫。


public String get(String key) {
String value = redis.get(key);
if (value == null) { //緩存值過期
String unique_key = systemId + ":" + key;
//設置30s的超時
if (redis.set(unique_key, 1, 'NX', 'PX', 30000) == 1) { //設置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(unique_key);
} else { //其他線程已經到數據庫取值并回寫到緩存了,可以重試獲取緩存值
sleep(50);
get(key); //重試
}
} else {
return value;
}
}
} else {
return value;
}
}
24. Redis 怎么實現消息隊列?
使用一個列表,讓生產者將任務使用LPUSH命令放進列表,消費者不斷用RPOP從列表取出任務。
BRPOP和RPOP命令相似,唯一的區別就是當列表沒有元素時BRPOP命令會一直阻塞連接,直到
有新元素加入。
優先級隊列
如果多個鍵都有元素,則按照從左到右的順序取元素。
發布/訂閱模式
PSUBSCRIBE channel?* 按照規則訂閱。 PUNSUBSCRIBE channel?* 退訂通過PSUBSCRIBE命令按照
某種規則訂閱的頻道。其中訂閱規則要進行嚴格的字符串匹配, PUNSUBSCRIBE * 無法退訂 channel?*
規則。
缺點:在消費者下線的情況下,生產的消息會丟失。
延時隊列
使用sortedset,拿時間戳作為score,消息內容作為key,調用zadd來生產消息,消費者用
zrangebyscore 指令獲取N秒之前的數據輪詢進行處理。
25. pipeline的作用?
redis客戶端執行一條命令分4個過程: 發送命令、命令排隊、命令執行、返回結果。使用 pipeline 可
以批量請求,批量返回結果,執行速度比逐條執行要快。
使用 pipeline 組裝的命令個數不能太多,不然數據量過大,增加客戶端的等待時間,還可能造成網絡
阻塞,可以將大量命令的拆分多個小的 pipeline 命令完成。
原生批命令(mset和mget)與 pipeline 對比:
- 原生批命令是原子性, pipeline 是非原子性。pipeline命令中途異常退出,之前執行成功的命令
不會回滾。
- 原生批命令只有一個命令,但 pipeline 支持多命令。
26. LUA腳本
Redis 通過 LUA 腳本創建具有原子性的命令: 當lua腳本命令正在運行的時候,不會有其他腳本或
Redis 命令被執行,實現組合命令的原子操作。
在Redis中執行Lua腳本有兩種方法: eval 和 evalsha 。 eval 命令使用內置的 Lua 解釋器,對 Lua 腳
本進行求值。
//第一個參數是lua腳本,第二個參數是鍵名參數個數,剩下的是鍵名參數和附加參數> eval "return
{KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second1) "key1"2) "key2"3)
"first"4) "second"
lua腳本作用
1、Lua腳本在Redis中是原子執行的,執行過程中間不會插入其他命令。
2、Lua腳本可以將多條命令一次性打包,有效地減少網絡開銷。
應用場景
舉例:限制接口訪問頻率。
在Redis維護一個接口訪問次數的鍵值對, key 是接口名稱, value 是訪問次數。每次訪問接口時,會
執行以下操作:
通過 aop 攔截接口的請求,對接口請求進行計數,每次進來一個請求,相應的接口訪問次數 count
加1,存入redis。
如果是第一次請求,則會設置 count=1 ,并設置過期時間。因為這里 set() 和 expire() 組合操作
不是原子操作,所以引入 lua 腳本,實現原子操作,避免并發訪問問題。
如果給定時間范圍內超過最大訪問次數,則會拋出異常。
private String buildLuaScript() {
return "local c" +
"\nc = redis.call('get',KEYS[1])" +
"\nif c and tonumber(c) > tonumber(ARGV[1]) then" +
"\nreturn c;" +
"\nend" +
"\nc = redis.call('incr',KEYS[1])" +
"\nif tonumber(c) == 1 then" +
"\nredis.call('expire',KEYS[1],ARGV[2])" +
"\nend" +
"\nreturn c;";
}
String luaScript = buildLuaScript();
RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript,
Number.class);
Number count = redisTemplate.execute(redisScript, keys, limit.count(),
limit.period());
PS:這種接口限流的實現方式比較簡單,問題也比較多,一般不會使用,接口限流用的比較多的是令牌
桶算法和漏桶算法。
27. 什么是RedLock?
Redis 官方站提出了一種權威的基于 Redis 實現分布式鎖的方式名叫 Redlock,此種方式比原先的單節點
的方法更安全。它可以保證以下特性:
-
安全特性:互斥訪問,即永遠只有一個 client 能拿到鎖
-
避免死鎖:最終 client 都可能拿到鎖,不會出現死鎖的情況,即使原本鎖住某資源的 client 掛掉了
-
容錯性:只要大部分 Redis 節點存活就可以正常提供服務

28.多級緩存

29.全局ID生成器


30.實現秒殺

1.超賣問題




2.一人一單












30.互相推送






浙公網安備 33010602011771號