redis問題及答案

 

2019.9.16更新:增加了對緩存雪崩,緩存穿透,緩存擊穿的描述。並附上本人對Redis單線程,多線程實現的理解。

評論區有一些爭執,但是大家都很友善,比如這個單線程是否能讓Redis更快尤其是大家爭論的重點。

(以下單線程僅指Redis負責存取這塊的線程只有一個,而非Redis中只有一個進程)

我先給個我的結論,單線程的Redis在瓶頸是cpu的io時(這不是大多數應用的實際應用場景),確實速度會比多線程慢。但是,我們實際應用場景中很少會遇到瓶頸是CPU的io的情況,這時候單線程優勢就凸顯出來了。

實現很簡單!性能又不會比多線程差,並且,單線程確實不用處理上下文的切換,cpu利用率會比多線程高,這時候採用單線程實現是一種很划算的做法。當然,如果你的寬帶和內存牛逼到了使得你的io成爲瓶頸,這時候也只能使用多線程了。

 

 

面試時考官讓我挑一種自己熟悉的NoSQL數據庫講一講,我當場就蒙了,我就用過sql server,mysql和Oracle這幾種,這幾種就算從名字看也知道是sql數據庫嘛,絞盡腦汁,我福至心靈,答出,Redis!

先說說Redis是什麼吧小老弟?

Redis嘛,就是一種運行速度很快,併發很強的跑在內存上的NoSql數據庫,支持鍵到五種數據類型的映射。

來來來,講一講爲什麼Redis這麼快?

首先,採用了多路複用io阻塞機制
然後,數據結構簡單,操作節省時間
最後,運行在內存中,自然速度快

Redis爲什麼是單線程的?

Redis官方很敷衍就隨便給了一點解釋。不過基本要點也都說了,因爲Redis的瓶頸不是cpu的運行速度,而往往是網絡帶寬和機器的內存大小。再說了,單線程切換開銷小,容易實現既然單線程容易實現,而且CPU不會成爲瓶頸,那就順理成章地採用單線程的方案了。

如果萬一CPU成爲你的Redis瓶頸了,或者,你就是不想讓服務器其他核閒置,那怎麼辦?

那也很簡單,你多起幾個Redis進程就好了。Redis是keyvalue數據庫,又不是關係數據庫,數據之間沒有約束。只要客戶端分清哪些key放在哪個Redis進程上就可以了。redis-cluster可以幫你做的更好。

單線程可以處理高併發請求嗎?

當然可以了,Redis都實現了。有一點概念需要澄清,併發並不是並行。
(相關概念:併發性I/O流,意味着能夠讓一個計算單元來處理來自多個客戶端的流請求。並行性,意味着服務器能夠同時執行幾個事情,具有多個計算單元)

我們使用單線程的方式是無法發揮多核CPU 性能,有什麼辦法發揮多核CPU的性能嘛?

我們可以通過在單機開多個Redis 實例來完善!
警告:這裏我們一直在強調的單線程,只是在處理我們的網絡請求的時候只有一個線程來處理,一個正式的Redis Server運行的時候肯定是不止一個線程的,這裏需要大家明確的注意一下!
例如Redis進行持久化的時候會以子進程或者子線程的方式執行(具體是子線程還是子進程待讀者深入研究)

簡述一下Redis值的五種類型

String 整數,浮點數或者字符串
Set 集合
Zset 有序集合
Hash 散列表
List 列表

 

 

有序集合的實現方式是哪種數據結構?

跳躍表。

請列舉幾個用得到Redis的常用使用場景?

緩存,毫無疑問這是Redis當今最爲人熟知的使用場景。再提升服務器性能方面非常有效;

排行榜,在使用傳統的關係型數據庫(mysql oracle 等)來做這個事兒,非常的麻煩,而利用Redis的SortSet(有序集合)數據結構能夠簡單的搞定;

計算器/限速器,利用Redis中原子性的自增操作,我們可以統計類似用戶點贊數、用戶訪問數等,這類操作如果用MySQL,頻繁的讀寫會帶來相當大的壓力;限速器比較典型的使用場景是限制某個用戶訪問某個API的頻率,常用的有搶購時,防止用戶瘋狂點擊帶來不必要的壓力;

好友關係,利用集合的一些命令,比如求交集、並集、差集等。可以方便搞定一些共同好友、共同愛好之類的功能;

簡單消息隊列,除了Redis自身的發佈/訂閱模式,我們也可以利用List來實現一個隊列機制,比如:到貨通知、郵件發送之類的需求,不需要高可靠,但是會帶來非常大的DB壓力,完全可以用List來完成異步解耦;

Session共享,以PHP爲例,默認Session是保存在服務器的文件中,如果是集羣服務,同一個用戶過來可能落在不同機器上,這就會導致用戶頻繁登陸;採用Redis保存Session後,無論用戶落在那臺機器上都能夠獲取到對應的Session信息。

一些頻繁被訪問的數據,經常被訪問的數據如果放在關係型數據庫,每次查詢的開銷都會很大,而放在redis中,因爲redis 是放在內存中的可以很高效的訪問

簡述Redis的數據淘汰機制

volatile-lru 從已設置過期時間的數據集中挑選最近最少使用的數據淘汰
volatile-ttl 從已設置過期時間的數據集中挑選將要過期的數據淘汰
volatile-random從已設置過期時間的數據集中任意選擇數據淘汰
allkeys-lru從所有數據集中挑選最近最少使用的數據淘汰
allkeys-random從所有數據集中任意選擇數據進行淘汰
noeviction禁止驅逐數據

Redis怎樣防止異常數據不丟失?

RDB 持久化
將某個時間點的所有數據都存放到硬盤上。
可以將快照複製到其它服務器從而創建具有相同數據的服務器副本。
如果系統發生故障,將會丟失最後一次創建快照之後的數據。
如果數據量很大,保存快照的時間會很長。
AOF 持久化
將寫命令添加到 AOF 文件(Append Only File)的末尾。
使用 AOF 持久化需要設置同步選項,從而確保寫命令同步到磁盤文件上的時機。這是因爲對文件進行寫入並不會馬上將內容同步到磁盤上,而是先存儲到緩衝區,然後由操作系統決定什麼時候同步到磁盤。有以下同步選項:
選項同步頻率always每個寫命令都同步everysec每秒同步一次no讓操作系統來決定何時同步
always 選項會嚴重減低服務器的性能;
everysec 選項比較合適,可以保證系統崩潰時只會丟失一秒左右的數據,並且 Redis 每秒執行一次同步對服務器性能幾乎沒有任何影響;
no 選項並不能給服務器性能帶來多大的提升,而且也會增加系統崩潰時數據丟失的數量
隨着服務器寫請求的增多,AOF 文件會越來越大。Redis 提供了一種將 AOF 重寫的特性,能夠去除 AOF 文件中的冗餘寫命令。

講一講緩存穿透,緩存雪崩以及緩存擊穿吧

緩存穿透:就是客戶持續向服務器發起對不存在服務器中數據的請求。客戶先在Redis中查詢,查詢不到後去數據庫中查詢。
緩存擊穿:就是一個很熱門的數據,突然失效,大量請求到服務器數據庫中
緩存雪崩:就是大量數據同一時間失效。
打個比方,你是個很有錢的人,開滿了百度雲,騰訊視頻各種雜七雜八的會員,但是你就是沒有netflix的會員,然後你把這些賬號和密碼發佈到一個你自己做的網站上,然後你有一個朋友每過十秒鐘就查詢你的網站,發現你的網站沒有Netflix的會員後打電話向你要。你就相當於是個數據庫,網站就是Redis。這就是緩存穿透。
大家都喜歡看騰訊視頻上的《水果傳》,但是你的會員突然到期了,大家在你的網站上看不到騰訊視頻的賬號,紛紛打電話向你詢問,這就是緩存擊穿
你的各種會員突然同一時間都失效了,那這就是緩存雪崩了。

放心,肯定有辦法解決的。
緩存穿透:
1.接口層增加校驗,對傳參進行個校驗,比如說我們的id是從1開始的,那麼id<=0的直接攔截;
2.緩存中取不到的數據,在數據庫中也沒有取到,這時可以將key-value對寫爲key-null,這樣可以防止攻擊用戶反覆用同一個id暴力攻擊
緩存擊穿:
最好的辦法就是設置熱點數據永不過期,拿到剛纔的比方里,那就是你買騰訊一個永久會員
緩存雪崩:
1.緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
2.如果緩存數據庫是分佈式部署,將熱點數據均勻分佈在不同搞得緩存數據庫中。

 

嗦一下Redis中的Master-Slave模式

連接過程

  1. 主服務器創建快照文件,發送給從服務器,並在發送期間使用緩衝區記錄執行的寫命令。快照文件發送完畢之後,開始向從服務器發送存儲在緩衝區中的寫命令;
  2. 從服務器丟棄所有舊數據,載入主服務器發來的快照文件,之後從服務器開始接受主服務器發來的寫命令;
  3. 主服務器每執行一次寫命令,就向從服務器發送相同的寫命令。

主從鏈

隨着負載不斷上升,主服務器可能無法很快地更新所有從服務器,或者重新連接和重新同步從服務器將導致系統超載。爲了解決這個問題,可以創建一箇中間層來分擔主服務器的複製工作。中間層的服務器是最上層服務器的從服務器,又是最下層服務器的主服務器。

 

 

 

Sentinel(哨兵)可以監聽集羣中的服務器,並在主服務器進入下線狀態時,自動從從服務器中選舉出新的主服務器。

 

分片

分片是將數據劃分爲多個部分的方法,可以將數據存儲到多臺機器裏面,這種方法在解決某些問題時可以獲得線性級別的性能提升。
假設有 4 個 Redis 實例 R0,R1,R2,R3,還有很多表示用戶的鍵 user:1,user:2,... ,有不同的方式來選擇一個指定的鍵存儲在哪個實例中。

最簡單的方式是範圍分片,例如用戶 id 從 0~1000 的存儲到實例 R0 中,用戶 id 從 1001~2000 的存儲到實例 R1 中,等等。但是這樣需要維護一張映射範圍表,維護操作代價很高。
還有一種方式是哈希分片,使用 CRC32 哈希函數將鍵轉換爲一個數字,再對實例數量求模就能知道應該存儲的實例。

根據執行分片的位置,可以分爲三種分片方式:
客戶端分片:客戶端使用一致性哈希等算法決定鍵應當分佈到哪個節點。
代理分片:將客戶端請求發送到代理上,由代理轉發請求到正確的節點上。
服務器分片:Redis Cluster
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章