大廠必問的Redis面試題

      網友投稿 759 2025-04-04

      本文目錄:

      Redis是什么?

      Redis優缺點?

      Redis為什么這么快?

      Redis為何選擇單線程

      Redis6.0為何引入多線程?

      Redis應用場景有哪些?

      Memcached和Redis的區別?

      Redis 數據類型有哪些?

      keys命令存在的問題?

      SortedSet和List異同點?

      Redis事務

      持久化機制

      RDB方式

      AOF方式

      RDB和AOF如何選擇?

      Redis常見的部署方式有哪些?

      主從復制

      哨兵Sentinel

      Redis cluster

      哈希分區算法有哪些?

      過期鍵的刪除策略?

      內存淘汰策略有哪些?

      如何保證緩存與數據庫雙寫時的數據一致性?

      緩存穿透

      緩存雪崩

      緩存擊穿

      Redis 怎么實現消息隊列?

      pipeline的作用?

      LUA腳本

      什么是RedLock?

      Redis是什么?

      Redis(Remote Dictionary Server)是一個使用 C 語言編寫的,高性能非關系型的鍵值對數據庫。與傳統數據庫不同的是,Redis 的數據是存在內存中的,所以讀寫速度非常快,被廣泛應用于緩存方向。Redis可以將數據寫入磁盤中,保證了數據的安全不丟失,而且Redis的操作是原子性的。

      Redis優缺點?

      優點:

      基于內存操作,內存讀寫速度快。

      Redis是單線程的,避免線程切換開銷及多線程的競爭問題。單線程是指網絡請求使用一個線程來處理,即一個線程處理所有網絡請求,Redis 運行時不止有一個線程,比如數據持久化的過程會另起線程。

      支持多種數據類型,包括String、Hash、List、Set、ZSet等。

      支持持久化。Redis支持RDB和AOF兩種持久化機制,持久化功能可以有效地避免數據丟失問題。

      支持事務。Redis的所有操作都是原子性的,同時Redis還支持對幾個操作合并后的原子性執行。

      支持主從復制。主節點會自動將數據同步到從節點,可以進行讀寫分離。

      缺點:

      對結構化查詢的支持比較差。

      數據庫容量受到物理內存的限制,不適合用作海量數據的高性能讀寫,因此Redis適合的場景主要局限在較小數據量的操作。

      Redis 較難支持在線擴容,在集群容量達到上限時在線擴容會變得很復雜。

      Redis為什么這么快?

      基于內存:Redis是使用內存存儲,沒有磁盤IO上的開銷。數據存在內存中,讀寫速度快。

      單線程實現( Redis 6.0以前):Redis使用單個線程處理請求,避免了多個線程之間線程切換和鎖資源爭用的開銷。

      IO多路復用模型:Redis 采用 IO 多路復用技術。Redis 使用單線程來輪詢描述符,將數據庫的操作都轉換成了事件,不在網絡I/O上浪費過多的時間。

      高效的數據結構:Redis 每種數據類型底層都做了優化,目的就是為了追求更快的速度。

      Redis為何選擇單線程

      避免過多的上下文切換開銷。程序始終運行在進程中單個線程內,沒有多線程切換的場景。

      避免同步機制的開銷:如果 Redis選擇多線程模型,需要考慮數據同步的問題,則必然會引入某些同步機制,會導致在操作數據過程中帶來更多的開銷,增加程序復雜度的同時還會降低性能。

      實現簡單,方便維護:如果 Redis使用多線程模式,那么所有的底層數據結構的設計都必須考慮線程安全問題,那么 Redis 的實現將會變得更加復雜。

      Redis6.0為何引入多線程?

      Redis支持多線程主要有兩個原因:

      可以充分利用服務器 CPU 資源,單線程模型的主線程只能利用一個cpu;

      多線程任務可以分攤 Redis 同步 IO 讀寫的負荷。

      Redis應用場景有哪些?

      緩存熱點數據,緩解數據庫的壓力。

      利用 Redis 原子性的自增操作,可以實現計數器的功能,比如統計用戶數、用戶訪問數等。

      簡單的消息隊列,可以使用Redis自身的發布/訂閱模式或者List來實現簡單的消息隊列,實現異步操作。

      限速器,可用于限制某個用戶訪問某個接口的頻率,比如秒殺場景用于防止用戶快速點擊帶來不必要的壓力。

      好友關系,利用集合的一些命令,比如交集、并集、差集等,實現共同好友、共同愛好之類的功能。

      Memcached和Redis的區別?

      Redis 只使用單核,而 Memcached 可以使用多核。

      MemCached 數據結構單一,僅用來緩存數據,而 Redis 支持多種數據類型。

      MemCached 不支持數據持久化,重啟后數據會消失。Redis 支持數據持久化。

      Redis 提供主從同步機制和 cluster 集群部署能力,能夠提供高可用服務。Memcached 沒有提供原生的集群模式,需要依靠客戶端實現往集群中分片寫入數據。

      Redis 的速度比 Memcached 快很多。

      Redis 使用單線程的多路 IO 復用模型,Memcached使用多線程的非阻塞 IO 模型。

      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 :主要用于存儲地理位置信息,并對存儲的信息進行操作,適用場景如定位、附近的人等。

      keys命令存在的問題?

      redis的單線程的。keys指令會導致線程阻塞一段時間,直到執行完畢,服務才能恢復。scan采用漸進式遍歷的方式來解決keys命令可能帶來的阻塞問題,每次scan命令的時間復雜度是O(1),但是要真正實現keys的功能,需要執行多次scan。

      scan的缺點:在scan的過程中如果有鍵的變化(增加、刪除、修改),遍歷過程可能會有以下問題:新增的鍵可能沒有遍歷到,遍歷出了重復的鍵等情況,也就是說scan并不能保證完整的遍歷出來所有的鍵。

      SortedSet和List異同點?

      相同點:

      都是有序的;

      都可以獲得某個范圍內的元素。

      不同點:

      列表基于鏈表實現,獲取兩端元素速度快,訪問中間元素速度慢;

      有序集合基于散列表和跳躍表實現,訪問中間元素時間復雜度是OlogN;

      列表不能簡單的調整某個元素的位置,有序列表可以(更改元素的分數);

      有序集合更耗內存。

      Redis事務

      事務的原理是將一個事務范圍內的若干命令發送給Redis,然后再讓Redis依次執行這些命令。

      事務的生命周期:

      使用MULTI開啟一個事務

      在開啟事務的時候,每次操作的命令將會被插入到一個隊列中,同時這個命令并不會被真的執行

      EXEC命令進行提交事務

      一個事務范圍內某個命令出錯不會影響其他命令的執行,不保證原子性:

      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 OK 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)

      比如上面的代碼中:

      watch name開啟了對name這個key的監控

      修改name的值

      開啟事務a

      在事務a中設置了name和gender的值

      使用EXEC命令進提交事務

      使用命令get gender發現不存在,即事務a沒有執行

      使用UNWATCH可以取消WATCH命令對key的監控,所有監控鎖將會被取消。

      持久化機制

      持久化就是把內存的數據寫到磁盤中,防止服務宕機導致內存數據丟失。

      Redis支持兩種方式的持久化,一種是RDB的方式,一種是AOF的方式。前者會根據指定的規則定時將內存中的數據存儲在硬盤上,而后者在每次執行完命令后將命令記錄下來。一般將兩者結合使用。

      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操作創建子進程,屬于重量級操作,頻繁執行成本比較高。

      RDB 文件使用特定二進制格式保存,Redis 版本升級過程中有多個格式的 RDB 版本,存在老版本 Redis 無法兼容新版 RDB 格式的問題。

      AOF方式

      AOF(append only file)持久化:以獨立日志的方式記錄每次寫命令,Redis重啟時會重新執行AOF文件中的命令達到恢復數據的目的。AOF的主要作用是解決了數據持久化的實時性,AOF 是Redis持久化的主流方式。

      默認情況下Redis沒有開啟AOF方式的持久化,可以通過appendonly參數啟用:appendonly yes。開啟AOF方式持久化后每執行一條寫命令,Redis就會將該命令寫進aof_buf緩沖區,AOF緩沖區根據對應的策略向硬盤做同步操作。

      默認情況下系統每30秒會執行一次同步操作。為了防止緩沖區數據丟失,可以在Redis寫入AOF文件后主動要求系統將緩沖區數據同步到硬盤上。可以通過appendfsync參數設置同步的時機。

      appendfsync always //每次寫入aof文件都會執行同步,最安全最慢,不建議配置 appendfsync everysec //既保證性能也保證安全,建議配置 appendfsync no //由操作系統決定何時進行同步操作

      接下來看一下 AOF 持久化執行流程:

      所有的寫入命令會追加到 AOP 緩沖區中。

      AOF 緩沖區根據對應的策略向硬盤同步。

      隨著 AOF 文件越來越大,需要定期對 AOF 文件進行重寫,達到壓縮文件體積的目的。AOF文件重寫是把Redis進程內的數據轉化為寫命令同步到新AOF文件的過程。

      當 Redis 服務器重啟時,可以加載 AOF 文件進行數據恢復。

      優點:

      AOF可以更好的保護數據不丟失,可以配置 AOF 每秒執行一次fsync操作,如果Redis進程掛掉,最多丟失1秒的數據。

      AOF以append-only的模式寫入,所以沒有磁盤尋址的開銷,寫入性能非常高。

      缺點:

      對于同一份文件AOF文件比RDB數據快照要大。

      數據恢復比較慢。

      RDB和AOF如何選擇?

      通常來說,應該同時使用兩種持久化方案,以保證數據安全。

      如果數據不敏感,且可以從其他地方重新生成,可以關閉持久化。

      如果數據比較重要,且能夠承受幾分鐘的數據丟失,比如緩存等,只需要使用RDB即可。

      如果是用做內存數據,要使用Redis的持久化,建議是RDB和AOF都開啟。

      如果只用AOF,優先使用everysec的配置選擇,因為它在可靠性和性能之間取了一個平衡。

      當RDB與AOF兩種方式都開啟時,Redis會優先使用AOF恢復數據,因為AOF保存的文件比RDB文件更完整。

      Redis常見的部署方式有哪些?

      Redis的幾種常見使用方式包括:

      單機版

      Redis主從

      Redis Sentinel(哨兵)

      Redis Cluster

      使用場景:

      單機版:很少使用。存在的問題:1、內存容量有限 2、處理能力有限 3、無法高可用。

      主從模式:master 節點掛掉后,需要手動指定新的 master,可用性不高,基本不用。

      哨兵模式:master 節點掛掉后,哨兵進程會主動選舉新的 master,可用性高,但是每個節點存儲的數據是一樣的,浪費內存空間。數據量不是很多,集群規模不是很大,需要自動容錯容災的時候使用。

      Redis cluster:主要是針對海量數據+高并發+高可用的場景,如果是海量數據,如果你的數據量很大,那么建議就用Redis cluster,所有主節點的容量總和就是Redis cluster可緩存的數據容量。

      主從復制

      Redis的復制功能是支持多個數據庫之間的數據同步。主數據庫可以進行讀寫操作,當主數據庫的數據發生變化時會自動將數據同步到從數據庫。從數據庫一般是只讀的,它會接收主數據庫同步過來的數據。一個主數據庫可以有多個從數據庫,而一個從數據庫只能有一個主數據庫。

      redis-server //啟動Redis實例作為主數據庫 redis-server --port 6380 --slaveof 127.0.0.1 6379 //啟動另一個實例作為從數據庫 slaveof 127.0.0.1 6379 SLAVEOF NO ONE //停止接收其他數據庫的同步并轉化為主數據庫。

      主從復制的原理?

      當啟動一個從節點時,它會發送一個 PSYNC 命令給主節點;

      如果是從節點初次連接到主節點,那么會觸發一次全量復制。此時主節點會啟動一個后臺線程,開始生成一份 RDB 快照文件;

      同時還會將從客戶端 client 新收到的所有寫命令緩存在內存中。RDB 文件生成完畢后, 主節點會將RDB文件發送給從節點,從節點會先將RDB文件寫入本地磁盤,然后再從本地磁盤加載到內存中;

      接著主節點會將內存中緩存的寫命令發送到從節點,從節點同步這些數據;

      如果從節點跟主節點之間網絡出現故障,連接斷開了,會自動重連,連接之后主節點僅會將部分缺失的數據同步給從節點。

      哨兵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 會推選出某個表現良好的從節點成為新的主節點,然后通知其他從節點更新主節點信息。

      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個數據庫空間。

      哈希分區算法有哪些?

      節點取余分區。使用特定的數據,如Redis的鍵或用戶ID,對節點數量N取余:hash(key)%N計算出哈希值,用來決定數據映射到哪一個節點上。

      優點是簡單性。擴容時通常采用翻倍擴容,避免數據映射全部被打亂導致全量遷移的情況。

      一致性哈希分區。為系統中每個節點分配一個token,范圍一般在0~232,這些token構成一個哈希環。數據讀寫執行節點查找操作時,先根據key計算hash值,然后順時針找到第一個大于等于該哈希值的token節點。

      這種方式相比節點取余最大的好處在于加入和刪除節點只影響哈希環中相鄰的節點,對其他節點無影響。

      虛擬槽分區,所有的鍵根據哈希函數映射到0~16383整數槽內,計算公式:slot=CRC16(key)&16383。每一個節點負責維護一部分槽以及槽所映射的鍵值數據。Redis Cluser采用虛擬槽分區算法。

      過期鍵的刪除策略?

      1、被動刪除。在訪問key時,如果發現key已經過期,那么會將key刪除。

      2、主動刪除。定時清理key,每次清理會依次遍歷所有DB,從db隨機取出20個key,如果過期就刪除,如果其中有5個key過期,那么就繼續對這個db進行清理,否則開始清理下一個db。

      3、內存不夠時清理。Redis有最大內存的限制,通過maxmemory參數可以設置最大內存,當使用的內存超過了設置的最大內存,就要進行內存釋放, 在進行內存釋放的時候,會按照配置的淘汰策略清理內存。

      內存淘汰策略有哪些?

      當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。

      如何保證緩存與數據庫雙寫時的數據一致性?

      1、先刪除緩存再更新數據庫

      進行更新操作時,先刪除緩存,然后更新數據庫,后續的請求再次讀取時,會從數據庫讀取后再將新數據更新到緩存。

      存在的問題:刪除緩存數據之后,更新數據庫完成之前,這個時間段內如果有新的讀請求過來,就會從數據庫讀取舊數據重新寫到緩存中,再次造成不一致,并且后續讀的都是舊數據。

      2、先更新數據庫再刪除緩存

      進行更新操作時,先更新MySQL,成功之后,刪除緩存,后續讀取請求時再將新數據回寫緩存。

      存在的問題:更新MySQL和刪除緩存這段時間內,請求讀取的還是緩存的舊數據,不過等數據庫更新完成,就會恢復一致,影響相對比較小。

      3、異步更新緩存

      數據庫的更新操作完成后不直接操作緩存,而是把這個操作命令封裝成消息扔到消息隊列中,然后由Redis自己去消費更新數據,消息隊列可以保證數據操作順序一致性,確保緩存系統的數據正常。

      緩存穿透

      緩存穿透是指查詢一個不存在的數據,由于緩存是不命中時被動寫的,如果從DB查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到DB去查詢,失去了緩存的意義。在流量大時,可能DB就掛掉了。

      緩存空值,不會查數據庫。

      采用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,查詢不存在的數據會被這個bitmap攔截掉,從而避免了對DB的查詢壓力。

      布隆過濾器的原理:當一個元素被加入集合時,通過K個散列函數將這個元素映射成一個位數組中的K個點,把它們置為1。查詢時,將元素通過散列函數映射之后會得到k個點,如果這些點有任何一個0,則被檢元素一定不在,直接返回;如果都是1,則查詢元素很可能存在,就會去查詢Redis和數據庫。

      緩存雪崩

      緩存雪崩是指在我們設置緩存時采用了相同的過期時間,導致緩存在某一時刻同時失效,請求全部轉發到DB,DB瞬時壓力過重掛掉。

      解決方法:在原有的失效時間基礎上增加一個隨機值,使得過期時間分散一些。

      緩存擊穿

      緩存擊穿:大量的請求同時查詢一個 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; } }

      Redis 怎么實現消息隊列?

      使用一個列表,讓生產者將任務使用LPUSH命令放進列表,消費者不斷用RPOP從列表取出任務。

      BRPOP和RPOP命令相似,唯一的區別就是當列表沒有元素時BRPOP命令會一直阻塞連接,直到有新元素加入。

      BRPOP queue 0 //0表示不限制等待時間

      優先級隊列

      如果多個鍵都有元素,則按照從左到右的順序取元素。

      BLPOP queue:1 queue:2 queue:3 0

      發布/訂閱模式

      PSUBSCRIBE channel?* 按照規則訂閱。

      PUNSUBSCRIBE channel?* 退訂通過PSUBSCRIBE命令按照某種規則訂閱的頻道。其中訂閱規則要進行嚴格的字符串匹配,PUNSUBSCRIBE *無法退訂channel?*規則。

      PUBLISH channel1 hi SUBSCRIBE channel1 UNSUBSCRIBE channel1 //退訂通過SUBSCRIBE命令訂閱的頻道。

      缺點:在消費者下線的情況下,生產的消息會丟失。

      延時隊列

      使用sortedset,拿時間戳作為score,消息內容作為key,調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒之前的數據輪詢進行處理。

      pipeline的作用?

      redis客戶端執行一條命令分4個過程: 發送命令、命令排隊、命令執行、返回結果。使用pipeline可以批量請求,批量返回結果,執行速度比逐條執行要快。

      使用pipeline組裝的命令個數不能太多,不然數據量過大,增加客戶端的等待時間,還可能造成網絡阻塞,可以將大量命令的拆分多個小的pipeline命令完成。

      原生批命令(mset和mget)與pipeline對比:

      原生批命令是原子性,pipeline是非原子性。pipeline命令中途異常退出,之前執行成功的命令不會回滾。

      原生批命令只有一個命令,但pipeline支持多命令。

      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 second 1) "key1" 2) "key2" 3) "first" 4) "second"

      大廠必問的Redis面試題

      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 redisScript = new DefaultRedisScript<>(luaScript, Number.class); Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period());

      PS:這種接口限流的實現方式比較簡單,問題也比較多,一般不會使用,接口限流用的比較多的是令牌桶算法和漏桶算法。

      什么是RedLock?

      Redis 官方站提出了一種權威的基于 Redis 實現分布式鎖的方式名叫 Redlock,此種方式比原先的單節點的方法更安全。它可以保證以下特性:

      安全特性:互斥訪問,即永遠只有一個 client 能拿到鎖

      避免死鎖:最終 client 都可能拿到鎖,不會出現死鎖的情況,即使原本鎖住某資源的 client 掛掉了

      容錯性:只要大部分 Redis 節點存活就可以正常提供服務

      碼字不易,如果覺得對你有幫助,可以點個贊鼓勵一下!

      我是程序員大彬 ,專注Java后端硬核知識分享,歡迎大家關注~

      Redis 數據結構

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:EXACT函數的應用及使用實例
      下一篇:怎么把空白的頁刪掉(怎么將空白頁刪除)
      相關文章
      亚洲av无码一区二区三区人妖| 亚洲乱码一二三四区麻豆| 亚洲成a人片在线观看精品| 亚洲网站视频在线观看| 曰韩亚洲av人人夜夜澡人人爽| 亚洲国产成人久久精品99| 亚洲精品无码久久久久秋霞| 亚洲日韩av无码中文| 亚洲一区二区三区丝袜| 亚洲精品美女网站| 国产亚洲精品影视在线| 亚洲国产日韩综合久久精品| 2020年亚洲天天爽天天噜| 亚洲xxxx18| 亚洲欧美国产国产综合一区| 亚洲欧美日韩中文无线码 | 亚洲av无码不卡| 国产亚洲美女精品久久久久狼| 亚洲韩国精品无码一区二区三区 | 亚洲午夜国产精品无码| 亚洲乱码无码永久不卡在线| 亚洲国产精品VA在线观看麻豆| 水蜜桃亚洲一二三四在线| 亚洲日本中文字幕| 亚洲熟妇色自偷自拍另类| 亚洲乱码一二三四五六区| 亚洲中文字幕无码爆乳app| 亚洲AV无码男人的天堂| 亚洲成a人片在线观看国产| 精品国产人成亚洲区| 亚洲VA中文字幕无码毛片| 久久久亚洲欧洲日产国码aⅴ| 亚洲欧洲精品一区二区三区| 亚洲中文字幕无码一去台湾| 亚洲精品乱码久久久久蜜桃| 精品久久久久亚洲| 亚洲一级特黄大片无码毛片| 久久亚洲综合色一区二区三区| 亚洲三级电影网址| 亚洲第一页中文字幕| 中文字幕乱码亚洲精品一区|