數據庫學習筆記:redis哨兵、持久化、主從、手撕LRU

技術是把雙刃劍嘛,集羣的引入也會帶來很多問題,如:集羣的高可用怎麼保證,數據怎麼同步等等

問題一:爲啥Redis快

我們可以先看一下關係型數據庫跟Redis本質上的區別。

                                             關係型數據庫                                                         redis

Redis 採用的是基於內存的採用的是單進程單線程模型的 KV 數據庫,由C語言編寫,官方提供的數據是可以達到100000+的QPS(每秒內查詢次數)

  • 完全基於內存,絕大部分請求是純粹的內存操作,非常快速。它的數據存在內存中,類似於HashMapHashMap的優勢就是查找和操作的時間複雜度都是O(1);

  • 數據結構簡單,對數據操作也簡單,Redis中的數據結構是專門進行設計的;

  • 採用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因爲可能出現死鎖而導致的性能消耗;

  • 使用多路I/O複用模型,非阻塞IO;

  • 使用底層模型不同,它們之間底層實現方式以及與客戶端之間通信的應用協議不一樣,Redis直接自己構建了VM 機制 ,因爲一般的系統調用系統函數的話,會浪費一定的時間去移動和請求;

問題二:什麼是上下文切換

我記得有過一個小夥伴問過我上下文切換是啥,爲啥可能會線程不安全。打個比方麼:我是這麼說的,就好比你看一本英文書,你看到第十頁發現有個單詞不會讀,你加了個書籤,然後去查字典,過了一會你又回來繼續從書籤那裏讀,ok到目前爲止沒啥問題。

如果是你一個人讀肯定沒啥問題,但是你去查的時候,別的小夥伴好奇你在看啥他就翻了一下你的書,然後溜了,哦豁,你再看的時候就發現書不是你看的那一頁了。不知道到這裏爲止我有沒有解釋清楚,以及爲啥會線程不安全,就是因爲你一個人怎麼看都沒事,但是人多了換來換去的操作一本書數據就亂了。可能我的解釋很粗糙,但是道理應該是一樣的。

問題二:那他是單線程的,我們現在服務器都是多核的,那不是很浪費?

是的他是單線程的,但是,我們可以通過在單機開多個Redis實例嘛。

既然提到了單機會有瓶頸,那你們是怎麼解決這個瓶頸的?

我們用到了集羣的部署方式也就是Redis cluster,並且是主從同步讀寫分離,類似Mysql的主從同步,Redis cluster 支撐 N 個 Redis master node,每個master node都可以掛載多個 slave node

這樣整個 Redis 就可以橫向擴容了。如果你要支撐更大數據量的緩存,那就橫向擴容更多的 master 節點,每個 master 節點就能存放更多的數據了。

哦?那問題就來了,他們之間是怎麼進行數據交互的?以及Redis是怎麼進行持久化的?Redis數據都在內存中,一斷電或者重啓不就木有了嘛?

是的,持久化的話是Redis高可用中比較重要的一個環節,因爲Redis數據在內存的特性,持久化必須得有,我瞭解到的持久化是有兩種方式的。

  • RDB:RDB 持久化機制,是對 Redis 中的數據執行週期性的持久化。

  • AOF:AOF 機制對每條寫入命令作爲日誌,以 append-only 的模式寫入一個日誌文件中,因爲這個模式是隻追加的方式,所以沒有任何磁盤尋址的開銷,所以很快,有點像Mysql中的binlog

兩種方式都可以把Redis內存中的數據持久化到磁盤上,然後再將這些數據備份到別的地方去,RDB更適合做冷備AOF更適合做熱備,比如我杭州的某電商公司有這兩個數據,我備份一份到我杭州的節點,再備份一個到上海的,就算髮生無法避免的自然災害,也不會兩個地方都一起掛吧,這災備也就是異地容災,地球毀滅他沒辦法。

tip:兩種機制全部開啓的時候,Redis在重啓的時候會默認使用AOF去重新構建數據,因爲AOF的數據是比RDB更完整的。

那這兩種機制各自優缺點是啥?

我先說RDB

優點:

他會生成多個數據文件,每個數據文件分別都代表了某一時刻Redis裏面的數據,這種方式,有沒有覺得很適合做冷備,完整的數據運維設置定時任務,定時同步到遠端的服務器,比如阿里的雲服務,這樣一旦線上掛了,你想恢復多少分鐘之前的數據,就去遠端拷貝一份之前的數據就好了。

RDBRedis的性能影響非常小,是因爲在同步數據的時候他只是fork了一個子進程去做持久化的,而且他在數據恢復的時候速度比AOF來的快。

缺點:

RDB都是快照文件,都是默認五分鐘甚至更久的時間纔會生成一次,這意味着你這次同步到下次同步這中間五分鐘的數據都很可能全部丟失掉。AOF則最多丟一秒的數據,數據完整性上高下立判。

還有就是RDB在生成數據快照的時候,如果文件很大,客戶端可能會暫停幾毫秒甚至幾秒,你公司在做秒殺的時候他剛好在這個時候fork了一個子進程去生成一個大快照,哦豁,出大問題。

我們再來說說AOF

優點:

上面提到了,RDB五分鐘一次生成快照,但是AOF是一秒一次去通過一個後臺的線程fsync操作,那最多丟這一秒的數據。

AOF在對日誌文件進行操作的時候是以append-only的方式去寫的,他只是追加的方式寫數據,自然就少了很多磁盤尋址的開銷了,寫入性能驚人,文件也不容易破損。

AOF的日誌是通過一個叫非常可讀的方式記錄的,這樣的特性就適合做災難性數據誤刪除的緊急恢復了,比如公司的實習生通過flushall清空了所有的數據,只要這個時候後臺重寫還沒發生,你馬上拷貝一份AOF日誌文件,把最後一條flushall命令刪了就完事了。

tip:我說的命令你們別真去線上系統操作啊,想試去自己買的服務器上裝個Redis試,別到時候來說,敖丙真是個渣男,害我把服務器搞崩了,Redis官網上的命令都去看看,不要亂試!!!

缺點:

一樣的數據,AOF文件比RDB還要大。

AOF開啓後,Redis支持寫的QPS會比RDB支持寫的要低,他不是每秒都要去異步刷新一次日誌嘛fsync,當然即使這樣性能還是很高,我記得ElasticSearch也是這樣的,異步刷新緩存區的數據去持久化,爲啥這麼做呢,不直接來一條懟一條呢,那我會告訴你這樣性能可能低到沒辦法用的,大家可以思考下爲啥喲。

那兩者怎麼選擇?

小孩子才做選擇,我全都要,你單獨用RDB你會丟失很多數據,你單獨用AOF,你數據恢復沒RDB來的快,真出什麼時候第一時間用RDB恢復,然後AOF做數據補全,真香!冷備熱備一起上,纔是互聯網時代一個高健壯性系統的王道。

看不出來年紀輕輕有點東西的呀,對了我聽你提到了高可用,Redis還有其他保證集羣高可用的方式麼?

!!!暈 自己給自己埋個坑(其實是明早就準備好了,故意拋出這個詞等他問,就怕他不問)。

假裝思考一會(不要太久,免得以爲你真的不會),哦我想起來了,還有哨兵集羣sentinel

哨兵必須用三個實例去保證自己的健壯性的,哨兵+主從並不能保證數據不丟失,但是可以保證集羣的高可用

爲啥必須要三個實例呢?我們先看看兩個哨兵會咋樣。

master宕機了 s1和s2兩個哨兵只要有一個認爲你宕機了就切換了,並且會選舉出一個哨兵去執行故障,但是這個時候也需要大多數哨兵都是運行的。

那這樣有啥問題呢?M1宕機了,S1沒掛那其實是OK的,但是整個機器都掛了呢?哨兵就只剩下S2個裸屌了,沒有哨兵去允許故障轉移了,雖然另外一個機器上還有R1,但是故障轉移就是不執行。

經典的哨兵集羣是這樣的:

M1所在的機器掛了,哨兵還有兩個,兩個人一看他不是掛了嘛,那我們就選舉一個出來執行故障轉移不就好了。

暖男我,小的總結下哨兵組件的主要功能:

  • 集羣監控:負責監控 Redis master 和 slave 進程是否正常工作。

  • 消息通知:如果某個 Redis 實例有故障,那麼哨兵負責發送消息作爲報警通知給管理員。

  • 故障轉移:如果 master node 掛掉了,會自動轉移到 slave node 上。

  • 配置中心:如果故障轉移發生了,通知 client 客戶端新的 master 地址。

我記得你還提到了主從同步,能說一下主從之間的數據怎麼同步的麼?

面試官您的記性可真是一級棒呢,我都要忘了你還記得,我特麼謝謝你,提到這個,就跟我前面提到的數據持久化的RDBAOF有着比密切的關係了。

我先說下爲啥要用主從這樣的架構模式,前面提到了單機QPS是有上限的,而且Redis的特性就是必須支撐讀高併發的,那你一臺機器又讀又寫,這誰頂得住啊,不當人啊!但是你讓這個master機器去寫,數據同步給別的slave機器,他們都拿去讀,分發掉大量的請求那是不是好很多,而且擴容的時候還可以輕鬆實現水平擴容。

迴歸正題,他們數據怎麼同步的呢?

你啓動一臺slave 的時候,他會發送一個psync命令給master ,如果是這個slave第一次連接到master,他會觸發一個全量複製。master就會啓動一個線程,生成RDB快照,還會把新的寫請求都緩存在內存中,RDB文件生成後,master會將這個RDB發送給slave的,slave拿到之後做的第一件事情就是寫進本地的磁盤,然後加載進內存,然後master會把內存裏面緩存的那些新命名都發給slave。

數據傳輸的時候斷網了或者服務器掛了怎麼辦啊?

傳輸過程中有什麼網絡問題啥的,會自動重連的,並且連接之後會把缺少的數據補上的。

大家需要記得的就是,RDB快照的數據生成的時候,緩存區也必須同時開始接受新請求,不然你舊的數據過去了,你在同步期間的增量數據咋辦?是吧?

那說了這麼多你能說一下他的內存淘汰機制麼,來手寫一下LRU代碼?

 

手寫LRU?你是不是想直接跳起來說一句:Are U F**k Kidding me?

這個問題是我在螞蟻金服三面的時候親身被問過的問題,不知道大家有沒有被懟到過這個問題。

Redis的過期策略,是有定期刪除+惰性刪除兩種。

定期好理解,默認100ms就隨機抽一些設置了過期時間的key,去檢查是否過期,過期了就刪了。

爲啥不掃描全部設置了過期時間的key呢?

假如Redis裏面所有的key都有過期時間,都掃描一遍?那太恐怖了,而且我們線上基本上也都是會設置一定的過期時間的。全掃描跟你去查數據庫不帶where條件不走索引全表掃描一樣,100ms一次,Redis累都累死了。

如果一直沒隨機到很多key,裏面不就存在大量的無效key了?

好問題,惰性刪除,見名知意,惰性嘛,我不主動刪,我懶,我等你來查詢了我看看你過期沒,過期就刪了還不給你返回,沒過期該怎麼樣就怎麼樣。

最後就是如果的如果,定期沒刪,我也沒查詢,那可咋整?

內存淘汰機制

官網上給到的內存淘汰機制是以下幾個:

  • noeviction:返回錯誤當內存限制達到並且客戶端嘗試執行會讓更多內存被使用的命令(大部分的寫入指令,但DEL和幾個例外)

  • allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新添加的數據有空間存放。

  • volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限於在過期集合的鍵,使得新添加的數據有空間存放。

  • allkeys-random: 回收隨機的鍵使得新添加的數據有空間存放。

  • volatile-random: 回收隨機的鍵使得新添加的數據有空間存放,但僅限於在過期集合的鍵。

  • volatile-ttl: 回收在過期集合的鍵,並且優先回收存活時間(TTL)較短的鍵,使得新添加的數據有空間存放。

    如果沒有鍵滿足回收的前提條件的話,策略volatile-lruvolatile-random以及volatile-ttl就和noeviction 差不多了。

至於LRU我也簡單提一下,手寫實在是太長了,大家可以去Redis官網看看,我把近視LUR效果給大家看看

tip:Redis爲什麼不使用真實的LRU實現是因爲這需要太多的內存。不過近似的LRU算法對於應用而言應該是等價的。使用真實的LRU算法與近似的算法可以通過下面的圖像對比。

LRU comparison

你可以看到三種點在圖片中, 形成了三種帶.

  • 淺灰色帶是已經被回收的對象。

  • 灰色帶是沒有被回收的對象。

  • 綠色帶是被添加的對象。

  • LRU實現的理論中,我們希望的是,在舊鍵中的第一半將會過期。RedisLRU算法則是概率的過期舊的鍵。

你可以看到,在都是五個採樣的時候Redis 3.0比Redis 2.8要好,Redis2.8中在最後一次訪問之間的大多數的對象依然保留着。使用10個採樣大小的Redis 3.0的近似值已經非常接近理論的性能。

注意LRU只是個預測鍵將如何被訪問的模型。另外,如果你的數據訪問模式非常接近冪定律,大部分的訪問將集中在一個鍵的集合中,LRU的近似算法將處理得很好。

其實在大家熟悉的LinkedHashMap中也實現了Lru算法的,實現如下:

 

當容量超過100時,開始執行LRU策略:將最近最少未使用的 TimeoutInfoHolder 對象evict 掉。

真實面試中會讓你寫LUR算法,你可別搞原始的那個,那真TM多,寫不完的,你要麼懟上面這個,要麼懟下面這個,找一個數據結構實現下Java版本的LRU還是比較容易的,知道啥原理就好了。

總結

好記性不如爛筆頭,實踐是檢驗真理的唯一標準,要多動手才能真的掌握知識哦,這裏推

薦幾本經典的Redis入門的書籍和我參考的資料。

  •  Redis中文官網

  • 《Redis入門指南(第2版)》

  • 《Redis實戰》

  • 《Redis 設計與實現——黃健宏》

  • 《Redis 深度歷險——錢文品》

  • 《億級流量網站架構核心技術——張開濤》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章