老司機帶你玩轉面試(1):緩存中間件 Redis 基礎知識以及數據持久化

引言

今天週末,我在家坐着掐指一算,立刻又要到一年一度的金九銀十招聘季了,國內今年上半年受到 YQ 衝擊,金三銀四泡湯了,這就直接致使不少今年畢業的同窗會和明年畢業的同窗一塊兒參加今年下半年的秋招,這個競爭就比較激烈了。java

最近後臺有一些朋友給我留言,但願我能寫寫招聘相關的內容,畢竟雖說是金九銀十,可是不少大公司的校招從 7 、 8 月份就開始了。git

原本是想寫點面試技巧和簡歷技巧的,但我轉念一想,你們都是搞技術的,問題的核心仍是技術能力要過關,面試技巧這東西最多隻能用做錦上添花,而技術能力過不去,機會送到手裏都抓不住。github

再說簡歷這件事兒,說實話,這個不是短期內能進行彌補的,以前我也有文章分享過我是怎麼挑簡歷的,無外乎先看學校,再參考下實習公司,項目經驗這一欄就看心情了。面試

尤爲是校招,學校放在第一位,在金礦裏淘金子確定要比在沙堆上淘金子效率高得多,固然,若有能有大廠的實習經驗,也會成爲簡歷上的一個亮點,能去大廠實習,無論最後什麼緣由沒有留下,自己已經能夠說明不少問題了。redis

MD ,老說實話,實話這麼講下去得罪的人有點多啊。docker

固然哈,若是這兩樣都沒有出彩的地方,就只能在項目經驗上下點功夫了,在 Github 上多找找能拿來練手的項目,多動動手,動起來總比成天怨天尤人要來的強。shell

第一份工做能夠找的很差,至少先混口飯吃,在工做中再接着學習,夯實本身的基礎,作個兩三年工做仍是能夠換的嘛。數據庫

Redis 安裝

扯遠了,強行扯回來,我思來想去仍是用 Redis 開頭,就是由於這玩意使用頻率以及使用場景太多了,無論什麼語言,作什麼方向,最終都能和 Redis 扯上關係(emmm,扯不上的我也能強行扯上)。緩存

先簡單介紹下我認爲的本地最簡安裝方案,這個方案順便還實現了跨平臺(一開始我並無想到這個,直到我寫到這才突然想起來)。安全

Docker + Redis ,Docker 在各個平臺都有本身的安裝包,請各位同窗去官網自行下載安裝,我就很少介紹了, Windows 環境下官網提供了現成的 exe 程序,直接雙擊一路 next 到底就完。

Docker 安裝成功後,無論在哪一個平臺都是打開命令行模式,或者找個輸入命令的地方,Windows 平臺下可使用 CMDPowerShell 或者 Windows Terminal 等工具,其餘常見平臺包括 MacOS 、 CentOS 或者 Ubuntu ,就不用我多說了吧,若是本身找不到的話,我估摸着我也找不到。

而後用下面三個命令安裝 Redis :

# 下載最新版本 Redis 鏡像
docker pull redis
# 查看當前鏡像
docker images
# 啓動命令
docker run --name redis -p 6380:6379  -d redis:latest --requirepass "02wKdSs7NvWT5TdlRyN4dxkXvIDnI1uroh5t"
# 查看運行容器
docker ps
# 進入容器
docker exec -it 013a252b24d6 redis-cli
複製代碼

稍微解釋下容器的啓動命令 --name 是對這個啓動的容器進行命名, -p 是指定映射的端口, -d 是指後臺運行容器,並返回容器的 ID , --requirepass 是指定了當前啓動的 Redis 的訪問密碼。

而後一個 Redis 就啓動好了,咱們進入容器執行幾條命令看下是否正常:

# 使用剛纔設置的密碼登陸
127.0.0.1:6379> auth 02wKdSs7NvWT5TdlRyN4dxkXvIDnI1uroh5t
OK
# 寫入一個 key-value
127.0.0.1:6379> set name geekdigging
OK
# 查詢 key
127.0.0.1:6379> get name
"geekdigging"
# 查詢全部 key
127.0.0.1:6379> keys *
1) "name"
# 刪除 key
127.0.0.1:6379> del name
(integer) 1
# 查詢全部 key
127.0.0.1:6379> keys *
(empty array)
# 退出
127.0.0.1:6379> quit
複製代碼

Redis 入門結束,看完這一段,在簡歷上寫個 Redis 達到了解級別我以爲木有任何問題。

基礎知識

既然是從面試出發,那麼接下來的內容將會以面試題爲導向進行解答,面試題來源於網絡或者我本身的杜撰。

爲何在項目中使用 Redis ?

如今基本上只要問到緩存,第一個問題基本上都是哪裏用了緩存?爲啥要用?不用行不行?用了之後會不會有什麼風險?

就是單純的看你背後對緩存有沒有思考,仍是說只是單純的傻乎乎的用。若是沒辦法給一個還能夠的回答,那這個映像分一下就降下來了。

首先哈,咱們在項目中使用 Redis 確定是爲了更高的性能和更好的併發。由於傳統的關係型數據庫已經沒法知足如今全部的使用場景了,最多見的秒殺場景,或者查詢時的流量洪峯等等,都很容易把傳統的 MySQL 或者是 Oracle 打崩,因此引入了緩存中間件 Redis 。

高性能:

假設一個場景,一個請求過來,開始查詢數據庫,亂七八糟一頓 SQL 操做,查了個結果,結果耗時可能有個 500ms 左右,就好比商城的首頁,各類類目的商品信息,各類推薦信息,若是走數據庫查詢,而且查完了可能接下來好幾個小時都沒什麼變化,那每次請求都走到數據庫裏,就有點不大合適了。

這時,咱們把查到的結果放到扔到緩存裏面,下次再來查詢,不走數據庫,直接走緩存查詢,算上網絡消耗可能 10ms 左右就能響應結果了,性能瞬間提高 50 倍。

這就是說,對於一些須要複雜操做耗時查出來的結果,且肯定後面不怎麼變化,可是有不少讀請求,那麼直接將查詢出來的結果放在緩存中,後面直接讀緩存就行了。

高併發:

MySQL 或者 Oracle 這種關係型數據庫壓根就不是用來玩併發的,雖然也能夠支撐必定的併發,單機 8C16G 的 MySQL 優化基本上極限能撐到 900 左右的 TPS , QPS 極限能撐到 9000 左右。別看這個數字不小,請注意是極限狀況,這個狀況下 CPU 全都已經爆表,整個服務已經處於不健康的狀態。

這時業務場景若是 1s 有 1w 的請求過來,使用一個 MySQL 單機確定直接崩掉,可是若是使用 Redis 緩存,把大量的熱點數據放在緩存,由於是走內存的操做,單機輕鬆支撐幾萬甚至於幾十萬的訪問。單機的併發承載量是 MySQL 的幾十倍。

除了 Redis 還有考慮過其餘緩存麼?

這個問題其實是在問知識廣度,由於如今市面上比較常見的緩存有兩個,一個是 Redis 還有一個是 Memcached ,而你們如今基本上都在用 Redis 而逐漸的拋棄掉了 Memcached ,這麼作確定是由緣由的,說明 Memcached 是存在明顯的短板的。

  1. Redis 相比較 Memcached 而言,它支持更復雜的數據結構,能支持更豐富的數據操做。
  2. Redis 在 3.x 版本之後,原生支持了集羣模式,而 Memcached 沒有原生的集羣模式,須要依靠客戶端來實現往集羣中分片寫入數據。
  3. Redis 擁有更加豐富的附加功能,如:pub/sub 功能, Lua 腳本支持, 序列化支持等等。
  4. Redis 支持數據持久化, RDB 和 AOF。

Redis 的線程模型是什麼?爲何 Redis 單線程卻能支撐高併發?

這兩個問題我放在一塊兒,其實是一個遞進的關係。

首先明確第一點, Redis 是單線程的模型。

而單線程卻能擁有很好的性能以及支撐高併發則得益於它自身的另外一套機制「 I/O 多路複用機制」。

Redis 內部使用文件事件處理器( file event handler ) 是單線程的,因此 Redis 才叫作單線程的模型。

它採用 IO 多路複用機制同時監聽多個 socket ,將產生事件的 socket 壓入內存隊列中,事件分派器根據 socket 上的事件類型來選擇對應的事件處理器進行處理。

儘管多個文件事件操做可能會併發的出現,但 I/O 多路複用系統老是會將全部產生的套接字( Socket ) 放到一個隊列裏面,而後經過這個隊列,以有序、同步、每次一個套接字的方式向文件分派器傳送套接字。只有當上一個套接字產生的事件被處理完畢以後, I/O 多路複用系統纔會繼續向文件分派器傳送下一個套接字。

同時,單線程的模型反而帶來了另外一個好處是無需頻繁的切換上下文,預防了多線程可能產生的競爭問題。

注意: Redis 6.0 以後的版本拋棄了單線程模型這一設計,本來使用單線程運行的 Redis 也開始選擇性地使用多線程模型。

前面還在強調 Redis 單線程模型的高效性,如今爲何又要引入多線程?這其實說明 Redis 在有些方面,單線程已經不具備優點了。由於讀寫網絡的 Read/Write 系統調用在 Redis 執行期間佔用了大部分 CPU 時間,若是把網絡讀寫作成多線程的方式對性能會有很大提高。

Redis 的多線程部分只是用來處理網絡數據的讀寫和協議解析,執行命令仍然是單線程。 之因此這麼設計是不想 Redis 由於多線程而變得複雜,須要去控制 key、lua、事務、LPUSH/LPOP 等等的併發問題。

Redis 的數據結構有哪些呀?

首先最基礎的五種數據結構必須爛熟於心:String 、 Hash 、 List 、 Set 、 SortedSet 。

若是連這五種基礎數據結構都記不住的話那就真的須要本身多補補課了。

那麼,是否是隻有這五種基礎數據結構,答案並非,好比在 2.8.9 版本添加的 HyperLogLog ,或者在 3.2 版本提供的 GEO ,又或者在 2.2 版本後新增的 Bitmap 以及在比較近的 5.0 版本新增的 Stream 。

  • HyperLogLog: 基數統計,這個結構能夠很是省內存的去統計各類計數,好比註冊 IP 數、每日訪問 IP 數、頁面實時UV)、在線用戶數等。可是它也有侷限性,就是隻能統計數量,而沒辦法去知道具體的內容是什麼。
  • GEO: 這個功能能夠將用戶給定的地理位置信息儲存起來, 並對這些信息進行操做。
  • Bitmap: BitMap 就是經過一個 bit 位來表示某個元素對應的值或者狀態, 其中的 key 就是對應元素自己,實際上底層也是經過對字符串的操做來實現。
  • Stream: 從功能層面來說, Streams 加上它的指令實現了一個完備的分佈式消息隊列。

上面這幾種種數據結構都不復雜,你們百度下看看文章就能和麪試官吹牛皮了。

能答出來後面這幾種數據類型,基本上面試官都會對你刮目相看,若是還能接着說出來使用場景以及具體應用,那麼這道題基本上你的發揮已經超出了面試官的預期。

這時你還能夠接着聊下去,好比你還用過一些 Redis 的第三方模塊,這個是 Redis 在 4.0 版本之後提供了插件功能,好比很是經常使用的一個插件布隆過濾器「 BloomFilter 」。

布隆過濾器主要是用來去重使用的,在空間上能夠節省 90% 以上,可是稍微有點不精確,有必定的誤判機率。能夠簡單的把布隆過濾器理解成一個不怎麼精確的 set 結構,當使用它的 contains 方案判斷某個對象是否存在時,它可能會誤判。可是布隆過濾器也不是特別不精確,只要參數設置的合理,它的精確度也能夠控制的相對足夠精確,只會有小小的誤判機率。

除了 「BloomFilter」 ,還有一些比較經常使用的,如: 「RedisSearch」 和 「rediSQL」 。

「RedisSearch」 是一個強大全文檢索插件而 「rediSQL」 則是一個使得 Redis 能使用 SQL 作查詢的插件。

Redis 的持久化有哪幾種方式?

Redis 數據持久化有兩種方式: RDB 和 AOF 。

  • RDB:RDB 持久化機制,是對 Redis 中的數據執行按期的持久化。
  • AOF: AOF 機制對每條寫入命令做爲日誌,以 append-only 的模式寫入一個日誌文件中,在 Redis 重啓的時候,能夠經過回放 AOF 日誌中的寫入指令來從新構建整個數據集。

經過 RDB 或者 AOF ,均可以將 Redis 內存中的數據持久化到硬盤上面,若是有須要,還能夠將硬盤上的數據備份到其餘地方去。

若是 Redis 掛了,這時止不只 Redis 服務掛掉,內存數據丟失,同時產生硬件損壞,硬盤上的數據也丟失或者復發恢復,咱們還能夠從其餘地方將數據拷貝回來進行數據恢復。

不一樣的持久化機制都有什麼優缺點?

RDB 的優缺點:

  • RDB 持久化既能夠經過手動執行,也能夠經過配置文件選項按期執行,它可使得某個時間點上的數據庫的狀態保存到一個 RDB 文件中。有兩個命令能夠用於生成 RDB 文件,一個是 save ,另外一個是 bgsavesave 命令會阻塞當前的 Redis 進程,直到 RDB 文件建立完畢,在這期間,服務器不能處理任何命令請求。而 bgsave 則會派生出一個子進程,而後由子進程生成 RDB 文件,服務進程不受干擾,繼續處理命令請求。
  • RDB 會生成多個數據文件,每一個數據文件都表明了某一個時刻中 Redis 的數據,這種多個數據文件的方式,很是適合作冷備,能夠將這種完整的數據文件發送到一些遠程的安全存儲上去。
  • RDB 對 Redis 對外提供的讀寫服務,影響很是小,可讓 Redis 保持高性能,由於 Redis 主進程只須要 fork 一個子進程,讓子進程執行磁盤 IO 操做來進行 RDB 持久化便可。
  • 相對於 AOF 持久化機制來講,直接基於 RDB 數據文件來重啓和恢復 Redis 進程,更加快速。
  • 在 Redis 故障時,儘量少的丟失數據,那麼 RDB 沒有 AOF 好。通常來講,RDB 數據快照文件,都是每隔 5 分鐘,或者更長時間生成一次,這個時候就得接受一旦 Redis 進程宕機,那麼會丟失最近 5 分鐘的數據。
  • RDB 每次在 fork 子進程來執行 RDB 快照數據文件生成的時候,若是數據文件特別大,可能會致使對客戶端提供的服務暫停數毫秒,或者甚至數秒。

AOF 的優缺點:

  • AOF 能夠更好的保護數據不丟失,通常 AOF 會每隔 1 秒,經過一個後臺線程執行一次 fsync 操做,最多丟失 1 秒鐘的數據。
  • AOF 日誌文件以 append-only 模式寫入,因此沒有任何磁盤尋址的開銷,寫入性能很是高,並且文件不容易破損,即便文件尾部破損,也很容易修復。
  • AOF 日誌文件即便過大的時候,出現後臺重寫操做,也不會影響客戶端的讀寫。由於在 rewrite log 的時候,會對其中的指令進行壓縮,建立出一份須要恢復數據的最小日誌出來。在建立新日誌文件的時候,老的日誌文件仍是照常寫入。當新的 merge 後的日誌文件 ready 的時候,再交換新老日誌文件便可。
  • AOF 日誌文件的命令經過可讀較強的方式進行記錄,這個特性很是適合作災難性的誤刪除的緊急恢復。
  • 對於同一份數據來講,AOF 日誌文件一般比 RDB 數據快照文件更大。
  • AOF 開啓後,支持的寫 QPS 會比 RDB 支持的寫 QPS 低,由於 AOF 通常會配置成每秒 fsync 一第二天志文件,固然,每秒一次 fsync ,性能也仍是很高的。(若是實時寫入,那麼 QPS 會大降,Redis 性能會大大下降)
  • 之前 AOF 發生過 bug,就是經過 AOF 記錄的日誌,進行數據恢復的時候,沒有恢復如出一轍的數據出來。因此說,相似 AOF 這種較爲複雜的基於命令日誌 / merge / 回放的方式,比基於 RDB 每次持久化一份完整的數據快照文件的方式,更加脆弱一些,容易有 bug。不過 AOF 就是爲了不 rewrite 過程致使的 bug,所以每次 rewrite 並非基於舊的指令日誌進行 merge 的,而是基於當時內存中的數據進行指令的從新構建,這樣健壯性會好不少。

兩種持久化方式選擇:

  • 不要僅僅使用 RDB,由於那樣會致使你丟失不少數據。
  • 也不要僅僅使用 AOF,由於那樣有兩個問題:第一,你經過 AOF 作冷備,沒有 RDB 作冷備來的恢復速度更快;第二,RDB 每次簡單粗暴生成數據快照,更加健壯,能夠避免 AOF 這種複雜的備份和恢復機制的 bug。
  • Redis 支持同時開啓開啓兩種持久化方式,咱們能夠綜合使用 AOF 和 RDB 兩種持久化機制,用 AOF 來保證數據不丟失,做爲數據恢復的第一選擇; 用 RDB 來作不一樣程度的冷備,在 AOF 文件都丟失或損壞不可用的時候,還可使用 RDB 來進行快速的數據恢復。

參考

github.com/doocs/advan…

您的掃碼關注,是對小編堅持原創的最大鼓勵:)
相關文章
相關標籤/搜索