Redis如何實現異步消息隊列?
List配合LPUSH和RPOP。
另外就是用 Redis 的 Pub/Sub 來實現簡單的消息廣播和訂閱。
但是這兩種方式都是不可靠的,因為沒有 ACK 機制所以不能保證訂閱者一定能收到消息,也不支持消息持久化。
Redis如何實現延時消息隊列?
延時消息隊列在實際業務中很常見,比如訂單超時取消、定時提醒等場景。
可以使用ZSet有序集合,將消息作為 member,把消息的需要執行時的時間戳作為 score。
這樣消息就會按照執行時間自動排序,消費者只需要定期掃描當前時間之前的消息進行處理就可以了。
具體來講,生產者向ZSet中發送消息時計算其應該執行的時間戳,然后通過ZADD命令將消息和時間戳添加到Zset;消費者用ZREMRANGEBYSCORE命令獲取當前時間戳之前的消息進行處理。
Redis支持事務嗎?
Redis支持簡單的事務,用MULTI開啟事務,將一系列命令放入執行隊列,中間可以用DISCARD取消事務隊列,放棄事務執行,最后用EXEC按照先進先出的順序執行隊列里的命令。
WATCH命令用于監視一個或多個key,如果這個key在事務最后執行之前被其它命令改動,那么事務將會被放棄執行
不支持回滾,只有執行和不執行的區別,也不支持多種隔離級別
說一下Redis事務的原理??
MULTI會將客戶端打一個事務的標記,表示先把命令存到隊列里,EXEC的時候再看情況執行(如果中間有DISCARD或者WATCH監控的key被其它命令修改則放棄執行事務),命令隊列按照FIFO執行,由于Redis是單線程的,天然支持原子性,不會被其它命令打斷?
Redis事務為什么不支持回滾?
沒有類似于MySQL那樣的undo log機制,設計理念是簡單高效而不是完整的ACID特性,引入回滾機制會增加復雜性和性能開銷
Redis事務滿足原子性嗎?要怎么改進??
Redis命令執行的實際過程中雖然不會被其它命令打斷,但是如果執行的過程中有報錯,后續的命令仍然會執行,事務不會被回滾,不符合傳統的原子性的定義。
使用Lua腳本代替事務實現報錯時回滾,可以在腳本中處理整個業務邏輯,包括條件檢查和錯誤處理,保證要么執行成功,要么回滾到初始狀態。
Redis事務的ACID特性如何實現?
單個 Redis 命令的執行是原子性的,但 Redis 沒有在事務上增加任何維持原子性的機制,所以 Redis 事務在執行過程中如果某個命令失敗了,其他命令還是會繼續執行,不會回滾。
一致性指的是,如果數據在執行事務之前是一致的,那么在事務執行之后,無論事務是否執行成功,數據也應該是一致的。但 Redis 事務并不保證一致性,因為如果事務中的某個命令失敗了,其他命令仍然會執行,就會出現數據不一致的情況。
Redis 是單線程執行事務的,并且不會中斷,直到執行完所有事務隊列中的命令為止。因此,我認為 Redis 的事務具有隔離性的特征。
Redis 事務的持久性完全依賴于 Redis 本身的持久化機制,如果開啟了 AOF,那么事務中的命令會作為一個整體記錄到 AOF 文件中,當然也要看 AOF 的 fsync 策略。
如果只開啟了 RDB,事務中的命令可能會在下次快照前丟失。如果兩個都沒有開啟,肯定是不滿足持久性的。
有Lua腳本操作Redis的經驗嗎?
用Lua腳本實現過分布式鎖,因為發現就算一個線程成功獲取了鎖,在設置過期時間的時候如果進程崩潰,由于Redis本身沒有回滾機制,這個鎖將永遠不會被釋放造成死鎖。其次,Lua腳本可以實現只有加鎖者才能釋放鎖的邏輯。
// 解鎖腳本特別重要,必須驗證是自己的鎖才能刪
private final String UNLOCK_SCRIPT = "if redis.call('GET', KEYS[1]) == ARGV[1] then " +" return redis.call('DEL', KEYS[1]) " +"else " +" return 0 " +"end";
Redis的管道Pipeline了解嗎?
Pipeline允許客戶端一次性向Redis服務器發送多個命令,不必等待一個命令執行完畢后再發送下一個。Redis服務器按照FIFO的順序執行命令,將所有結果打包發回客戶端。
什么場景下適合使用Pipeline?
需要批量插入、刪除、更新數據時,或者需要執行大量相似命令的時候,比如批量加載熱點數據,統計數據的批量更新、大批量數據的導入導出、批量刪除過期或無效的緩存
了解過Pipeline的底層原理嗎?
底層是緩沖的思想,我在RedisClient類中封裝了一個PipelineAction 內部類,用來緩存命令。
PipelineAction的add方法將命令封裝成Runnable對象,放入List中。execute方法調用RedisTemplate的executePipeline方法開啟管道模式將多個命令發送給Redis客戶端。
Redis從輸入緩沖區中讀取到命令后,依次執行這些命令,將執行結果寫入輸出緩沖區,最后再將結果一次性打包返回給客戶端。
Redis分布式鎖如何解決鎖過期問題
具體場景是,A獲取到了分布式鎖并設置了過期時間,但是由于執行的時間過長,任務還沒執行完畢鎖就被釋放了,其他線程就獲取到了鎖,但此時如果A執行完畢,釋放掉的其實是B的鎖。
可以通過鎖的自動續期機制來解決鎖過期的問題,比如Redission的看門狗機制,后臺啟動一個定時任務,隔一段時間就檢查鎖是否還被當前線程持有,如果是則延遲鎖的持有時間,這樣避免了鎖被提前釋放
Redisson了解多少??
Redission是基于Redis的java客戶端,它不只是對Redis進行簡單封裝,還提供了很多分布式的數據結構和服務,比如最常用的分布式鎖?。
Redisson 的分布式鎖比 SETNX 完善的得多,它的看門狗機制可以讓我們在獲取鎖的時候省去手動設置過期時間的步驟,它在內部封裝了一個定時任務,每隔 10 秒會檢查一次,如果當前線程還持有鎖就自動續期 30 秒。
另外,Redisson 還提供了分布式限流器 RRateLimiter,基于令牌桶算法實現,用于控制分布式環境下的訪問頻率。
詳細說說Redission的看門狗機制?
Redisson 的看門狗機制是一種自動續期機制,用于解決分布式鎖的過期問題。
基本原理是這樣的:當調用?lock()
?方法加鎖時,如果沒有顯式設置過期時間,Redisson 會默認給鎖加一個 30 秒的過期時間,同時啟用一個名為“看門狗”的定時任務,每隔 10 秒(默認是過期時間的 1/3),去檢查一次鎖是否還被當前線程持有,如果是,就自動續期,將過期時間延長到 30 秒。
續期時,Redission?Lua 腳本會檢查鎖的 value 是否匹配當前線程,如果匹配就延長過期時間。這樣就能保證只有鎖的真正持有者才能續期。
當調用unlock()時,看門狗任務結束,但是如果執行完任務忘記unlock了,看門狗也會自動檢查鎖,如果當前線程已經退出了或者鎖已經不屬于當前線程了,也會停止自動續期
看門狗機制中的鎖檢查和續期過程是原子操作嗎??
在Redis層面是原子的,通過Lua腳本實現原子的批量操作,不會被其他命令打斷,hexists命令和expire操作放在同一個Lua腳本中,是不可分割的,是原子的,要么都成功,要么回滾,確保續期的一定是當前線程持有的鎖
RedLock你了解多少??
是一種分布式鎖算法,用于解決單個Redis實例用作分布式鎖的時候存在的單點故障問題。
通過在多個完全獨立的Redis實例上同時獲取鎖來實現容錯,只要成功的實例數量超過一半就認為獲取鎖成功。
RedLock能不能保證百分百上鎖?
不能,由于網絡分區,因為客戶端不能保證和集群中所有的Redis實例建立連接,比如集群中有五個節點,但是客戶端只能和兩個建立連接,永遠也無法成功獲取半數以上的鎖,從而一定失敗。
如果各個 Redis 實例之間存在明顯的時鐘漂移,或者客戶端在獲取鎖的過程中耗時過長,比如網絡延遲、GC 停頓等,都可能會導致鎖在獲取完成前就過期,從而獲取失敗。
項目中有用到Redis分布式鎖嗎?
有使用到Redis分布式鎖防止用戶多端登陸同時大量調用人臉識別攻擊操作繞過閾值檢查