1、redis應用場景有哪些?
資料快取
redis訪問速度塊、支援的資料型別比較豐富,所以redis很適合用來儲存熱點資料,另外結合expire,我們可以設定過期時間然後再進行快取更新操作。
共享session
出於負載均衡的考慮,分散式服務會將使用者資訊的訪問均衡到不同伺服器上,使用者重新整理一次訪問可能會需要重新登入,為避免這個問題可以用redis將使用者session集中管理,在這種模式下只要保證redis的高可用和擴充套件性的,每次獲取使用者更新或查詢登入資訊都直接從redis中集中獲取。
限時限速業務
redis中可以使用expire命令設定一個鍵的生存時間,到時間後redis會刪除它。
應用:限時的優惠活動資訊、限制使用者獲取手機驗證碼的頻率
計數器
redis的incrby命令可以實現原子性的遞增
應用:運用於高併發的秒殺活動、分散式序列號id的生成、限制介面的呼叫次數
排行榜
redis的SortedSet有序集合的score是排序的依據。
分散式鎖
Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在時,為 key 設定指定的值。設定成功,返回 1 。 設定失敗,返回 0
應用:分散式定時任務,兩臺伺服器具有同樣定時任務,要求2小時執行一次(只允許一臺伺服器上定時任務執行,不能重複執行)。透過setnx設定一個key-value,執行Setnx dsrw _key run ,如果設定成果,則執行定時任務,如果設定失敗,則表明該定時任務已執行。並且可以給這個key設定一個過期時間,2小時。
佇列
redis有list push和list pop這樣的命令,所以能夠很方便的執行佇列操作
2、使用過Redis分散式鎖麼,它是什麼?
先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放。
但是在setnx之後執行expire之前程序意外crash或者要重啟維護了,會造成鎖永遠得不到釋放了。
原因:
Redis的setnx命令是當key不存在時設定key,但setnx不能同時完成expire設定失效時長,不能保證setnx和expire的原子性。
解決辦法:
使用set命令完成setnx和expire的操作,保證操作是原子性的。從 Redis 2。6。12 版本開始,set指令有非常複雜的引數,可以同時把setnx和expire合成一條指令來用
set key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:設定失效時長,單位秒
PX milliseconds:設定失效時長,單位毫秒
NX:key不存在時設定value,成功返回OK,失敗返回(nil)
XX:key存在時設定value,成功返回OK,失敗返回(nil)
3、Redis裡面有1億個key,其中有10w個key是以某個固定的已知的字首開頭的,如何將它們全部找出來?
KEYS 指令
KEYS pattern #用法
KEYS t?? #查詢如two,ttt這類的key
KEYS * #查詢所有key
Redis的單執行緒的。KEYS命令一次性返回所有匹配的key,keys指令會導致執行緒阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。
所以應該在生產環境禁止用使用keys和類似的命令smembers,這種時間複雜度為O(N),且會阻塞主執行緒的命令,是非常危險的。
SCAN 指令
每次執行都只會返回少量元素,所以可以用於生產環境,而不會出現像 KEYS 或者 SMEMBERS 命令帶來的可能會阻塞伺服器的問題。
SCAN命令是一個基於遊標的迭代器。這意味著命令每次被呼叫都需要使用上一次這個呼叫返回的遊標作為該次呼叫的遊標引數,以此來延續之前的迭代過程
當SCAN命令的遊標引數(即cursor)被設定為 0 時, 伺服器將開始一次新的迭代, 而當伺服器向用戶返回值為 0 的遊標時, 表示迭代已結束。
scan缺陷:
在對鍵進行增量式迭代的過程中, 鍵可能會被修改, 所以增量式迭代命令只能對被返回的元素提供有限的保證 。
scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重複機率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。
4、使用過Redis做非同步佇列麼,你是怎麼用的?
方式一:生產者消費者模式
使用list結構作為佇列,rpush生產訊息,lpop消費訊息,當lpop沒有訊息的時候,要適當sleep一會再重試。
如果不想使用sleep的話,可以使用blpop指令,在沒有訊息的時候,它會阻塞住直到訊息到。
方式二:釋出訂閱者模式
使用pub/sub主題訂閱者模式,可以實現1:N的訊息佇列。
缺點:在消費者下線的情況下,生產的訊息會丟失。此場景,建議使用MQ。
5、redis如何實現延時佇列
延遲佇列可以使用 zset(有序列表)實現,我們將訊息序列化成一個字串作為列表的value,這個訊息的到期處理時間作為score。
zset會按照時間戳大小進行排序,也就是對執行時間前後進行排序,這樣的話,起一個執行緒輪詢取第一個key值,如果當前時間戳大於等於該key值的socre就將它取出來進行消費刪除,就可以達到延時執行的目的, 注意不需要遍歷整個Zset集合,以免造成效能浪費。
6、如果有大量的key需要設定同一時間過期,一般需要注意什麼?
如果大量的key過期時間設定的過於集中,到過期的那個時間點,redis可能會出現短暫的卡頓現象。嚴重的話會出現快取雪崩,一般需要在時間上加一個隨機值,使得過期時間分散一些。
7、Redis如何做持久化的?
因為Redis的資料都儲存在記憶體中,當程序退出時,所有資料都將丟失。為了保證資料安全,Redis支援RDB和AOF兩種持久化機制有效避免資料丟失問題。RDB可以看作在某一時刻Redis的快照(snapshot),非常適合災難恢復。AOF則是寫入操作的日誌。
RDB做映象全量持久化,AOF做增量持久化。因為RDB會耗費較長時間,不夠實時,在停機的時候會導致大量丟失資料,所以需要AOF來配合使用。在redis例項重啟時,會使用RDB持久化檔案重新構建記憶體,再使用AOF重放近期的操作指令來實現完整恢復重啟之前的狀態。
如果突然機器掉電會怎樣?
取決於AOF日誌sync(同步)屬性的配置,如果不要求效能,在每條寫指令時都sync一下磁碟,就不會丟失資料。但是在高效能的要求下每次都sync是不現實的,一般都使用定時sync,比如1s1次,這個時候最多就會丟失1s的資料。
RDB的原理是什麼?
RDB就像是一臺給Redis記憶體資料儲存拍照的照相機,生成快照儲存到磁碟的過程。觸發RDB持久化分為手動觸發和自動觸發。Redis重啟讀取RDB速度快,但是無法做到實時持久化。支援手動觸發和自動觸發。
手動觸發分別對應save和bgsave命令:
1、save命令:同步,在主執行緒中儲存快照;阻塞當前Redis伺服器,直到RDB過程完成為止,對於記憶體比較大的例項會造成長時間阻塞,線上環境不建議使用;
2、bgsave命令:非同步,執行該命令時,Redis會在後臺非同步進行快照操作。具體操作是Redis程序執行fork操作建立子程序,RDB持久化過程由子程序負責,完成後自動結束。阻塞只發生在fork階段,一般時間很短。
3、BGSAVE命令是針對SAVE堵塞問題做的最佳化。因此Redis內部所有的設計RDB的操作都採用BGSAVE的方式,而save命令已經廢棄。
4、 fork操作:雖然fork同步操作是非常快的,但是如果需要同步的資料量過大,fork就會阻塞redis主程序;記憶體越大,fork同步資料耗時越長,當然也跟伺服器有關
自動觸發,在redis。conf檔案中配置save m n
指定當m秒內發生n次變化時,會自動觸發bgsave
AOF的原理是什麼?
AOF的出現很好的解決了資料持久化的實時性,AOF以獨立日誌的方式記錄每次寫命令,重啟時再重新執行AOF檔案中的命令來恢復資料。AOF會先把命令追加在AOF緩衝區,然後根據對應策略寫入硬碟(appendfsync)
AOF持久化方式的優點:
做到最多丟失1-2s內的資料(最多丟失2s資料,因為AOF追加阻塞)
AOF持久化方式的缺點:
AOF檔案比RDB檔案大
可能導致追加阻塞
PS:
如果AOF檔案fsync同步時間大於2s,Redis主程序就會阻塞;
如果AOF檔案fsync同步時間小於2s,Redis主程序就會返回;
其實這樣做的目的是為了保證檔案安全性的一種策略
8、Pipeline有什麼好處,為什麼要用pipeline?
未使用Pipeline
redis 執行一次操作所需要的時間:1 次時間 = 1 次網路時間 + 1次命令時間
執行 n 次就需要:n 次時間 = n 次網路時間 + n 次命令時間
使用Pipeline
由於命令時間非常短,影響時間開銷的主要是網路時間,所以我們可以把一組命令打包,然後一次傳送過去。這樣的話,時間開銷就變為:1 次 pipeline(n條命令) = 1 次網路時間 + n 次命令時間
pipeline 的好處
省略由於單執行緒導致的命令排隊時間,一次命令的消耗時間=一次網路時間 + 命令執行時間
比起命令執行時間,網路時間很可能成為系統的瓶頸
pipeline的作用是將一批命令進行打包,然後傳送給伺服器,伺服器執行完按順序打包返回。
透過pipeline,一次pipeline(n條命令)=一次網路時間 + n次命令時間
pipeline注意事項
每次pipeline攜帶數量不推薦過大,否則會影響網路效能
pipeline每次只能作用在一個Redis節點上
9、Redis的同步機制瞭解麼?
Redis透過主從同步機制來確保master和salve之間的資料同步。
Redis的主從複製功能分為兩種資料同步模式進行:全量資料同步和增量資料同步。
全量複製
redis全量複製一般發生在Slave初始化階段,這時Slave需要將Master上的所有資料都複製一份。
具體步驟如下:
從伺服器連線主伺服器,傳送SYNC命令;
主伺服器接收到SYNC命名後,開始執行BGSAVE命令生成RDB檔案並使用緩衝區記錄此後執行的所有寫命令;
主伺服器BGSAVE執行完後,向所有從伺服器傳送快照檔案,並在傳送期間繼續記錄被執行的寫命令;
從伺服器收到快照檔案後丟棄所有舊資料,載入收到的快照;
主伺服器快照發送完畢後開始向從伺服器傳送緩衝區中的寫命令;
從伺服器完成對快照的載入,開始接收命令請求,並執行來自主伺服器緩衝區的寫命令
增量同步
Redis增量複製是指Slave初始化後開始正常工作時主伺服器發生的寫操作同步到從伺服器的過程。
增量複製的過程主要是主伺服器每執行一個寫命令就會向從伺服器傳送相同的寫命令,從伺服器接收並執行收到的寫命令。
具體步驟如下:
redis 2。8前,當主從伺服器之間的連線斷掉之後,master伺服器和slave伺服器之間都是進行全量資料同步。
從redis2。8開始,引入了部分同步的概念,即使主從連線中途斷掉,slave伺服器不需要進行全量同步。
部分同步的實現依賴於在master伺服器記憶體中給每個slave伺服器維護了一份同步日誌和同步標識,每個slave伺服器在跟master伺服器進行同步時都會攜帶自己的同步標識和上次同步的最後位置。
當主從連線斷掉之後,slave伺服器隔斷時間(預設1s)主動嘗試和master伺服器進行連線。
如果從伺服器攜帶的偏移量標識還在master伺服器上的同步備份日誌中,那麼就從slave傳送的偏移量開始繼續上次的同步操作。
如果slave傳送的偏移量已經不再master的同步備份日誌中(可能由於主從之間斷掉的時間比較長或者在斷掉的短暫時間內master伺服器接收到大量的寫操作),則必須進行一次全量更新。在部分同步過程中,master會將本地記錄的同步備份日誌中記錄的指令依次傳送給slave伺服器從而達到資料一致。
Redis主從同步策略
主從剛剛連線的時候,進行全量同步;全同步結束後,進行增量同步。當然,如果有需要,slave 在任何時候都可以發起全量同步。redis 策略是,無論如何,首先會嘗試進行增量同步,如不成功,要求從機進行全量同步
10、什麼是快取雪崩?
由於原有快取失效,新快取未到期間。(例如:我們設定快取時採用了相同的過期時間,在同一時刻出現大面積的快取過期),所有原本應該訪問快取的請求都去查詢資料庫了,而對資料庫CPU和記憶體造成巨大壓力,嚴重的會造成資料庫宕機。從而形成一系列連鎖反應,造成整個系統崩潰。
解決辦法:
1、使用鎖或佇列
一般併發量不是特別多的時候,使用最多的解決方案是加鎖排隊,加鎖排隊只是為了減輕資料庫的壓力,並沒有提高系統吞吐量。假設在高併發下,快取重建期間key是鎖著。在真正的高併發場景下很少使用。
2、新增快取標記
給每一個快取資料增加相應的快取標記,記錄快取的是否失效,如果快取標記失效,則更新資料快取。
快取標記:記錄快取資料是否過期,如果過期會觸發通知另外的執行緒在後臺去更新實際key的快取;
快取資料:它的過期時間比快取標記的時間延長1倍,例:標記快取時間30分鐘,資料快取設定為60分鐘。 這樣,當快取標記key過期後,實際快取還能把舊資料返回給呼叫端,直到另外的執行緒在後臺更新完成後,才會返回新快取。
3、為key設定不同的快取失效時間
還有一個簡單方案就是在快取的時候給過期時間加上一個隨機值,將快取失效時間分散開。
4、雙快取
快取A和快取B
11、如果Redis掛掉了,請求全部走資料庫這種情況?
前面是由於快取大面積失效,這裡由於Redis掛掉導致請求全部走資料庫,導致資料庫服務可能崩潰。
事發前:實現Redis的高可用(主從架構+Sentinel 或者Redis Cluster),儘量避免Redis掛掉這種情況發生。
事發中:萬一Redis真的掛了,我們可以設定本地快取(ehcache)+限流(hystrix),儘量避免我們的資料庫被幹掉(起碼能保證我們的服務還是能正常工作的)
事發後:redis持久化,重啟後自動從磁碟上載入資料,快速恢復快取資料
12、什麼是快取預熱?
快取預熱就是系統上線後,提前將相關的快取資料直接載入到快取系統。避免在使用者請求的時候,先查詢資料庫,然後再將資料快取的問題!使用者直接查詢事先被預熱的快取資料!
快取預熱解決方案:
(1)直接寫個快取重新整理頁面,上線時手工操作下;
(2)資料量不大,可以在專案啟動的時候自動進行載入;
(3)定時重新整理快取;
13、什麼是快取更新?
LRU/LFU/FIFO演算法剔除:剔除演算法通常用於快取使用量超過了預設的最大值時候,如何對現有的資料進行剔除。例如Redis使用maxmemory-policy這個配置作為記憶體最大值後對於資料的剔除策略。
超時剔除:透過給快取資料設定過期時間,讓其在過期時間後自動刪除,例如Redis提供的expire命令。如果業務可以容忍一段時間內,快取層資料和儲存層資料不一致,那麼可以為其設定過期時間。在資料過期後,再從真實資料來源獲取資料,重新放到快取並設定過期時間。例如一個影片的描述資訊,可以容忍幾分鐘內資料不一致,但是涉及交易方面的業務,後果可想而知。
主動更新:應用方對於資料的一致性要求高,需要在真實資料更新後,立即更新快取資料。例如可以利用訊息系統或者其他方式通知快取更新。
14、什麼是快取降級?
當訪問量劇增、服務出現問題(如響應時間慢或不響應)或非核心服務影響到核心流程的效能時,仍然需要保證服務還是可用的,即使是有損服務。系統可以根據一些關鍵資料進行自動降級,也可以配置開關實現人工降級。
降級的最終目的是保證核心服務可用,即使是有損的。而且有些服務是無法降級的(如加入購物車、結算)。
以參考日誌級別設定預案:
(1)一般:比如有些服務偶爾因為網路抖動或者服務正在上線而超時,可以自動降級;
(2)警告:有些服務在一段時間內成功率有波動(如在95~100%之間),可以自動降級或人工降級,併發送告警;
(3)錯誤:比如可用率低於90%,或者資料庫連線池被打爆了,或者訪問量突然猛增到系統能承受的最大閥值,此時可以根據情況自動降級或者人工降級;
(4)嚴重錯誤:比如因為特殊原因資料錯誤了,此時需要緊急人工降級。
服務降級的目的,是為了防止Redis服務故障,導致資料庫跟著一起發生雪崩問題。因此,對於不重要的快取資料,可以採取服務降級策略,例如一個比較常見的做法就是,Redis出現問題,不去資料庫查詢,而是直接返回預設值給使用者。
15、單執行緒的redis為什麼這麼快
1、純記憶體操作
redis是基於記憶體的,記憶體的讀寫速度非常快。
Redis為了達到最快的讀寫速度將資料都讀到記憶體中,並透過非同步的方式將資料寫入磁碟。所以Redis具有快速和資料持久化的特性。
特例:
Redis 混合儲存例項是阿里雲自主研發的相容Redis協議和特性的雲資料庫產品,混合儲存例項突破 Redis 資料必須全部儲存到記憶體的限制,使用磁碟儲存全量資料,並將熱資料快取到記憶體,實現訪問效能與儲存成本的完美平衡。
2、單執行緒操作,避免了頻繁的上下文切換
上下文切換就是cpu在多執行緒之間進行輪流執行(搶佔cpu資源),而redis單執行緒的,因此避免了繁瑣的多執行緒上下文切換。
3、採用了非阻塞I/O多路複用機制
多路-指的是多個socket連線,複用-指的是複用一個執行緒。
傳統的多程序併發模型中 ,每一個新的I/O流會分配一個新的程序管理。
I/O多路複用 ,單個執行緒,透過記錄跟蹤每個I/O流的狀態,來同時管理多個I/O流 。
I/O多路複用就透過一種機制,監視多個描述符,一旦某個描述符就緒,能夠通知程式進行相應的操作。具體由select、poll和epoll實現