轉自:https://blog.csdn.net/Tmeng521/article/details/91039391
具體實現地址本篇文件是基於網絡上知識的整合
redis 是一個基於內存的高性能 key-value數據庫
支持豐富的數據類型(String,List , Set ,Sorted Set,Hash )
redis中的單個value的存儲限制是1G,比 Memcached的1MB要強大太多
哎呀,還是以問答的方式來寫這篇文章吧!
1.redis有什麼優缺點
redis是內存數據庫,所以當數據量達到一定程度的時候,單機版的必然是其瓶頸所在,這個時候我們就需要引入主從複製方案,用了主從複製之後我們就會發現,主庫值負責讀寫,從庫負責讀,數據量達到千萬級的時候寫的庫還是扛不住,所以就更加深入的使用了集羣。
2.redis的線程模型
redis內部使用的是文件事件處理器方式(file event handler)它是一個單線程的,所以redis也是單線程模型的關於這個文件事件處理器方式可參照下圖
此圖形象的標出了redis存儲原理
- 客戶端 socket01 向 redis 的 server socket 請求建立連接,此時 server socket 會產生一個
AE_READABLE
事件,IO 多路複用程序監聽到 server socket 產生的事件後,將該事件壓入隊列中。文件事件分派器從隊列中獲取該事件,交給連接應答處理器
。連接應答處理器會創建一個能與客戶端通信的 socket01,並將該 socket01 的AE_READABLE
事件與命令請求處理器關聯。 -
假設此時客戶端發送了一個
set key value
請求,此時 redis 中的 socket01 會產生AE_READABLE
事件,IO 多路複用程序將事件壓入隊列,此時事件分派器從隊列中獲取到該事件,由於前面 socket01 的AE_READABLE
事件已經與命令請求處理器關聯,因此事件分派器將事件交給命令請求處理器來處理。命令請求處理器讀取 socket01 的key value
並在自己內存中完成key value
的設置。操作完成後,它會將 socket01 的AE_WRITABLE
事件與令回覆處理器關聯。 -
如果此時客戶端準備好接收返回結果了,那麼 redis 中的 socket01 會產生一個
AE_WRITABLE
事件,同樣壓入隊列中,事件分派器找到相關聯的命令回覆處理器,由命令回覆處理器對 socket01 輸入本次操作的一個結果,比如ok
,之後解除 socket01 的AE_WRITABLE
事件與命令回覆處理器的關聯。
很簡單,多讀幾篇,就可理解。
3.爲什麼redis單線程效率也能那麼高
3.1.純內存操作,數據什麼的都存在服務器內存中(使用的時候要注意設置服務器可分不配給redis的內存大小,並且根據業務使用持久化機制)
3.2.非阻塞的IO多路複用機制
3.3.單線程避免了多線程情況下的上下文切換問題(內部使用了隊列技術,避免了傳統DB控制串行的開銷)
4.redis有幾種持久化方式
4.1RDB
rdb是全量持久化,是在配置文件中指定持久化的間隔時間,然後將內存中的數據集快照寫入磁盤,實際操作是fork一個子進程然後將數據集寫入一個臨時磁盤,字後覆蓋掉以前的數據集文件。
優點,可以靈活的設置備份頻率和週期因爲是自己設置的,並且因爲備份的一個數據集文件,所以當redis宕機的時候很容易就可以備份,內部使用了一個子進程所以在持久化的時候可以保證redis的高性能。恢復數據比AOF要快
缺點 :因爲rdb是以時間爲單位存儲的,比如每5分鐘寫一次,那麼在第4分鐘59秒的時候redis服務器宕機了那麼在這4分多鐘裏面的數據就會丟失。嚴重還是影響蠻大的。
4.2AOF
aof可以帶來更高的數據安全性,aof中有3中同步策略,(1.每秒同步 2.沒執行一個修改命令就同步3.不同步)
每秒同步是異步執行了,效率高但如果redis宕機了那麼那一秒的數據就丟了
每修改同步。redis中每次有寫的操作,都會同步到磁盤,效率比上面的每秒同步要低一些,但是極度安全
持久化的時候採用的是append的方式,由此可見恢復策略做的比較好,不會出現持久化文件錯亂問題,並且這個寫入磁盤的效率是非常之高,沒有磁盤尋址操作(kafka的消息存儲也是這個乾的,追加寫的形式)
如果日誌過大,Redis可以自動啓用 rewrite 機制。即使出現後臺重寫操作,也不會影響客戶端的讀寫。因爲在 rewrite log 的時候,會對其中的指令進行壓縮,創建出一份需要恢復數據的最小日誌出來。
缺點
同數據量下aof文件比rdb文件要大的多,因爲它記錄的是所有的寫指令,並且恢復速度也慢一點
總結:使用的時候可以同時開啓兩種持久化方式,利用aof來保證數據不丟失,在aof無法使用的時候,在用reb的備份文件做替補恢復,在兩種模式都開啓的情況下,默認採取aof來進行數據恢復
5.redis有幾種數據過期策略
5.1被動刪除:當讀/寫 一個過期的key時,會觸發惰性刪除策略,直接刪除這個key
5.2主動刪除由於惰性策略無法保證冷數據已經被刪除,所以redis會定期主動刪除一些已過期的key,或者當內存達到爲redis服務器設置的最大內存的時候,會主動的刪除一些數據(觸發數據淘汰策略)
6.什麼是redis的數據淘汰策略
- volatile-lru
- volatile-ttl
- volatile-random
- allkeys-lru
- allkeys-random
- no-enviction
7. MySQL 裏有 2000w 數據,Redis 中只存 20w 的數據,如何保證 Redis 中的數據都是熱點數據?
設置過期策略爲volatile-lru 或者allkeys-lru 這樣就會刪除不常用的,留下來的就是熱點數據了。
8.redis回收進程工作原理
1.在寫入redis之前查看是已經達到最大內存了,如果達到了就走數據淘汰策略,然後在寫入
9.redis如何解決緩存雪崩,緩存穿透,緩存擊穿
緩存雪崩呢就表示 在某個時間段設置了過期時間的key都一起失效了,那麼db肯定扛不住那麼大的併發量,可能會崩潰,所以我們在設置一些熱點key的時候,要儘量設置不同的過期時間,以避免在高併發的情況下緩存雪崩問題
緩存穿透呢 從字面上理解就是因爲每次做查詢請求的時候無法從緩存中查詢,從而直接將請請求落入DB,剛好db中也沒這條數據,那麼返回的就是null,就不會放入緩存中,周而復始知道db炸裂。 這種方案簡單粗暴的解決方式就是當db中查詢爲null的時候自己給個設置過期時間 的默認值,從而解決掉這種查詢直接到達db的頻率
緩存擊穿,某個熱點key突然過期了,那麼必然會導致,大量的請求入db那麼db就會扛不住壓力,涼涼。解決方案設置鎖,如果是分佈式系統就設置分佈式鎖。
10.redis如何實現分佈式鎖呢
實際上要理解的原理就是 設置key的過期時間,然後有請求到來的時候,去redis寫入這個key,如果這個key已經被寫入redis說明此鎖已經被佔用了,那麼就循環就如等待,上一個佔用鎖的人,在處理完邏輯後會釋放掉鎖,那麼下一個人就可以獲取這個鎖瞭然後執行自己的邏輯,總結來就是分佈式系統下的強制串行操作。
11.redis使用場景
1數據緩存(不做過多介紹)
2.手機驗證碼多長時間過期(可通過設置key的過期時間實現)
3.積分排行(設置key的過期時間,然後定時做統計)
4.計算器(單線程計數,不考慮併發情況下的數據不一致問題)
5.限流(通過設置某個ip的在1分鐘內訪問次數,來達到限流效果)
6.消息隊列
7.分佈式鎖
8.熱門列表(根據訪問次數來進行統計)
9.限制登錄次數(如果密碼錯了就計算1次,設置一個過期時間,連錯5次就鎖定此賬號)
10.唯一登錄,在系統登錄成功後記錄這個賬號信息,如果此賬號在其他地方又登錄,就清除某個token信息
11.token信息保存,剛好對應sprong-session中的核心處理
12.後續關於
主從複製的原理和實現
哨兵機制的原理和實現
redis事務的原理和實現
發佈訂閱機制的原理和實現
rdb和aof和實現
以及幾個使用場景的實現
https://github.com/LxyTe/redis
13記錄一點關於redis和db的數據一致性問題。
這個問題讀者應該在面試的時候都會被問題,其實這個已經涉及到了兩個數據庫了,已經算作分佈式事務的領域了。下面來說下解決方案。
方案一 沒有使用MQ的系統(併發寫量小的系統 )
先更新數據庫,數據庫更新完成之後,更新緩存(直接將結果給緩存不在查詢db)。(直接更新,不是刪除),缺點,數據庫如若成功,redis更新失敗,數據就不一致了。
方案二 使用了MQ的系統
先更新數據庫,數據庫更新成功返回狀態,然後把更新redis的操作使用MQ異步發出,然後使用MQ的消費者機制來保證一定會消費,從而避免由redis更新失敗帶來 數據不一致。(直接將結果給緩存不在查詢db)
方案三(併發量特別大的情況(這裏的併發只的是併發寫。),如果一個key的修改頻率大於查詢,那麼就要考慮這個key是否 適合緩存)
這個時候使用方案二會出現一個弊端,那就是在你使用MQ消費的時候是肯定存在延遲的,這個時候如果db的值已經改變那麼就會造成數據不一致,那麼這個時候第二步的做法就可以修改爲 消息到達mq只有,從db中查詢一次,然後將結果重新放在redis中
方案四 沒有使用MQ,系統併發量寫太大,但是併發讀不是很大,因爲刪緩存,併發讀又太大那麼容易造成緩存穿透)
那麼可以直接使用先更新數據庫,然後刪除緩存(等緩存使用的時候,從db中查詢,按需加載)
方案五 併發寫很大,併發讀也很大(秒殺系統(下單,那麼就要減庫存,下單前要判斷是否還與庫存))
此種解決方案的核心點是限流,通過各種限流將併發量減小,如果達到db能承受的範圍,在達到db能承受的範圍之後,將請求下放的數據庫層,然後先減去庫存,然後修改成功,在修改redis的數值(實際上的做法是先在redis層做限流,然後減去redis的庫存,然後再到db去修改。那麼這個時候就不用管redis的庫存了。)
總結:redis和db的數據一致性在不同的情景下有不同的做法,上面的五種場景第1種是用的最多的,如果對數據一致性比較重視,那麼可以用第二種,第三種的使用場景適用於少量寫,大量讀,情況下,數據的相對一致性。
分佈式系統下的數據一致性其實是可以有一點不一致的(據說12306那種級別的網站,也是在每天晚上的時候,做一次數據同步,將db中的數據,重新刷到緩存中,爲什麼要重新呢,這裏就不多解釋了吧,肯定是db和緩存數據不一致呀),因爲分佈式系統訪問的時候肯定會出現網絡延遲,有網絡延遲,肯定會出現數據不一致,以前有個同學和我說過, 如果你的系統連一點點數據一致性偏差都不能承受,那你還玩什麼分佈式系統呢。
14.如何解決rediskey傾斜問題
什麼是熱點key傾斜?就是在redis集羣中某個節點一直承受這大量的查詢操作,壓力非常大,而其他節點壓力都較小。這個時候需要解決這個壓力太大的問題
解決思路有兩種
1.在將一些特別熱點的key直接放在客戶端進行存儲,設置過期時間,過期後從後臺查詢。
2.上面的方法可以解決熱點key的傾斜問題,但是如果存儲在前端的數據在同一個時間都過期了呢,那麼還是有大量的請求到達redis的某一個節點,這個時候我們可以將一個熱點key 複製出多分子key,每個子key的value值一樣,查詢的時候使用hash取模算法,將壓力分配到不同的節點