你說一下Redis爲何快吧,怎麼實現高可用,還有持久化怎麼作的?

前言

做爲Java程序員,在面試過程當中,緩存相關的問題是躲不掉的,確定會問,例如緩存一致性問題,緩存雪崩、擊穿、穿透等。說到緩存,那確定少不了Redis,我在面試的時候也是被問了不少關於Redis相關的知識,可是Redis的功能太強大了,並非一時半會兒能掌握好的,由於有些高級特性或是知識平時並不會用到。
因此回答的很差,人家就會以爲你對本身平時使用的工具都沒有了解,天然就涼涼了。其實很早就有這個打算,打算好好總結一下Redis的知識,但也是因爲本身都沒有好好的瞭解Redis呢,因此一直沒有開始。此次準備慢慢的來總結。程序員

Redis爲何這麼快

Redis是一個由C語言編寫的開源的,基於內存,支持多種數據結構可持久化的NoSQL數據庫。
它速度快主要是有如下幾個緣由:面試

  • 基於內存運行,性能高效;
  • 數據結構設計高效,例如String是由動態字符數組構成,zset內部的跳錶;
  • 採用單線程,避免了線程的上下文切換,也避免了線程競爭產生的死鎖等問題;
  • 使用I/O多路複用模型,非阻塞IO;

官網上給出單臺Redis的能夠達到10w+的QPS的, 一臺服務器上在使用Redis的時候單核的就夠了,可是目前服務器都是多核CPU,要想不浪費資源,又能提交效率,能夠在一臺服務器上部署多個Redis實例。redis

高可用方案

雖然單臺Redis的的性能很好,可是Redis的單節點並不能保證它不會掛了啊,畢竟單節點的Redis是有上限的,並且人家單節點又要讀又要寫,小身板扛不住咋辦,因此爲了保證高可用,通常都是作成集羣。算法

主從(Master-Slave)

Redis官方是支持主從同步的,並且還支持從從同步,從從同步也能夠理解爲主從同步,只不過從從同步的主節點是另外一個主從的從節點。
在這裏插入圖片描述
有了主從同步的集羣,那麼主節點就負責提供寫操做,而從節點就負責支持讀操做。數據庫

那麼他們之間是如何進行數據同步的呢?

若是Slave(從節點)是第一次跟Master進行鏈接,數組

  • 那麼會首先會向Master發送同步請求psync
  • 主節點接收到同步請求,開始fork主子進程開始進行全量同步,而後生成RDB文件;
  • 這個時候主節點同時會將新的寫請求,保存到緩存區(buffer)中;
  • 從節點接收到RDB文件後,先清空老數據,而後將RDB中數據加載到內存中;
  • 等到從節點將RDB文件同步完成後再同步緩存區中的寫請求。

這裏有一點須要注意的就是,主節點的緩存區是有限的,內部結構是一個環形數組,當數組被佔滿以後就會覆蓋掉最先以前的數據。緩存

因此若是因爲網絡或是其餘緣由,形成緩存區中的數據被覆蓋了,那麼當從節點處理完主節點的RDB文件後,就不得不又要進行一全量的RDB文件的複製,才能保證主從節點的數據一致。安全

若是不設置好合理的buffer區空間,是會形成一個RDB複製的死循環。bash

當主從間的數據同步完成以後,後面主節點的每次寫操做就都會同步到從節點,這樣進行增量同步了。服務器

因爲負載的不斷上升就致使了主從之間的延時變大,因此就有了上面我說的從從同步了,主節點先同步到一部分從節點,而後由從節點去同步其餘的從節點。

在Redis從2.8.18開始支持無盤複製,主節點經過套接字,一邊遍歷內存中的數據,一邊讓數據發送給從節點,從節點和以前同樣,先將數據存儲在磁盤文件中,而後再一次性加載。

另外因爲主從同步是異步的,因此從Redis3.0以後出現了同步複製,就是經過wait命令來進行控制,wait命令有兩個參數,第一個是從庫數量,第二個是等待從庫的複製時間,若是第二個參數設置爲0,那麼就是表明要等待全部從庫都複製完纔去執行後面的命令。
可是這樣就會存在一個隱患,當網絡異常後,wait命令會一直阻塞下去,致使Redis不可用。

哨兵(Sentinel)

哨兵能夠監控Redis集羣的健康狀態,當主節點掛掉以後,選舉出新的主節點。客戶端在使用Redis的時候會先經過Sentinel來獲取主節點地址,而後再經過主節點來進行數據交互。當主節點掛掉以後,客戶端會再次向Sentinel獲取主節點,這樣客戶端就能夠無感知的繼續使用了。
在這裏插入圖片描述
哨兵集羣工做過程,主節點掛掉以後會選舉出新的主節點,而後監控掛掉的節點,當掛掉的節點恢復後,原先的主節點就會變成從節點,重新的主節點那裏創建主從關係。
在這裏插入圖片描述

集羣分片(Redis Cluster)

Redis Cluster是Redis官方推薦的集羣模式,Redis Cluster將全部數據劃分到16384個槽(slots)中,每一個節點負責一部分槽位的讀寫操做。

在這裏插入圖片描述

存儲

Redis Cluster默認是經過CRC16算法獲取到key的hash值,而後再對16384進行取餘(CRC16(key)%16384),獲取到的槽位在哪一個節點負責的範圍內(這裏通常是會有一個槽位和節點的映射表來進行快速定位節點的,一般使用bitmap來實現),就存儲在哪一個節點上。

重定向

當Redis Cluster的客戶端在和集羣創建鏈接的時候,也會得到一份槽位和節點的配置關係(槽位和節點的映射表),這樣當客戶端要查找某個key時,能夠直接定位到目標節點。

可是當客戶端發送請求時,若是接收請求的節點發現該數據的槽位並不在當前節點上,那麼會返回MOVED指令將正確的槽位和節點信息返回給客戶端,客戶接着請求正確的節點獲取數據。

通常客戶端在接收到MOVED指令後,也會更新本身本地的槽位和節點的映射表,這樣下次獲取數據時就能夠直接命中了。這整個重定向的過程對客戶端是透明的。

數據遷移

當集羣中新增節點或刪除節點後,節點間的數據遷移是按槽位爲單位的,一個槽位一個槽位的遷移,當遷移時原節點狀態處於:magrating,目標節點處於:importing

在遷移過程當中,客戶端首先訪問舊節點,若是數據還在舊節點,那麼舊節點正常處理,若是不在舊節點,就會返回一個-ASK + 目標節點地址的指令,客戶端收到這個-ASK指令後,向目標節點執行一個asking指令(告訴新節點,必須處理客戶端這個數據),而後再向目標節點執行客戶端的訪問數據的指令。
在這裏插入圖片描述

容錯

Redis Cluster能夠爲每一個主節點設置多個從節點,當單個主節點掛掉後,集羣會自動將其中某個從節點提高爲主節點,若沒有從節點,那麼集羣將處於不可用狀態。
Redis提供了一個參數:cluster-require-full-coverage,用來配置能夠容許部分節點出問題後,還有其餘節點在運行時能夠正常提供服務。

另一點比較特殊的是,Cluster中當一個節點發現某個其餘節點出現失聯了,這個時候問題節點只是PFailPossibly-可能下線),而後它會把這個失聯信息廣播給其餘節點,當一個節點接收到某個節點的失聯信息達到集羣的大多數時,就能夠將失聯節點標記爲下線,而後將下線信息廣播給其餘節點。若失聯節點爲主節點,那麼將當即對該節點進行主從切換。

Redis高可用就先說到這裏吧,後面其實還有Codis,可是目前Cluster逐漸流行起來了,Codis的競爭力逐漸被蠶食,並且對新版本的支持,更新的也比較慢,因此這裏就不說它了,感興趣的能夠本身去了解一下,國人開源的Redis集羣模式Codis。

持久化

Redis持久化的意義在於,當出現宕機問題後,能將數據恢復到緩存中,它提供了兩種持久化機制:一種是快照(RDB),一種是AOF日誌。

快照是一次全量備份,而AOF是增量備份。快照是內存數據的二進制序列化形式,存儲上很是緊湊,而AOF日誌記錄的是內存數據修改的指令記錄文本。

快照備份(RDB)

由於Redis是單線程的,因此在作快照持久化的時候,一般有兩個選擇,save命令,會阻塞線程,直到備份完成;bgsave會異步的執行備份,實際上是fork出了一個子進程,用子進程去執行快照持久化操做,將數據保存在一個.rdb文件中。

子進程剛剛產生的時候,是和父進程共享內存中的數據的,可是子進程作持久化時,是不會修改數據的,而父進程是要持續提供服務的,因此父進程就會持續的修改內存中的數據,這個時候父進程就會將內存中的數據,Copy出一份來進行修改。

父進程copy的數據是以數據頁爲單位的(4k一頁),對那一頁數據進行修改就copy哪一頁的數據。

子進程因爲數據沒有變化就會一直的去遍歷數據,進程持久化操做了,這就是隻保留了建立子進程的時候的快照。

那麼RDB是在何時觸發的呢?

# save <seconds> <changes>
save 60 10000
save 300 10

上這段配置就是在redis.conf文件中配置的,第一個參數是時間單位是秒,第二個參數執行數據變化的次數。
意思就是說:300秒以內至少發生10次寫操做、
60秒以內發生至少10000次寫操做,只要知足任一條件,均會觸發bgsave

增量日誌備份(AOF)

Redis在接收到客戶端請求指令後,會先進行校驗,校驗成功後,當即將指令存儲到AOF日誌文件中,就是說,Redis是先記錄日誌,再執行命令。這樣即便命令還沒執行忽然宕機了,經過AOF日誌文件也是能夠恢復的。

AOF重寫

AOF日誌文件,隨着時間的推移,會愈來愈大,因此就須要進行重寫瘦身。AOF重寫的原理就是,fork一個子進程,對內存進行遍歷,而後生成一系列的Redis指令,而後序列化到一個新的aof文件中。而後再將遍歷內存階段的增量日誌,追加到新的aof文件中,追加完成後當即替換舊的aof文件,這樣就完成了AOF的瘦身重寫

fsync

由於AOF是一個寫文件的IO操做,是比較耗時。因此AOF日誌並非直接寫入到日誌文件的,而是先寫到一個內核的緩存中,而後經過異步刷髒,來將數據保存到磁盤的。

因爲這個狀況,就致使了會有還沒來得急刷髒而後就宕機了,致使數據丟失的風險。

因此Redis提供了一個配置,能夠手動的來選擇刷髒的頻率。

  • always:每一條AOF記錄都當即同步到文件,性能很低,但較爲安全。
  • everysec每秒同步一次,性能和安全都比較中庸的方式,也是redis推薦的方式。若是遇到物理服務器故障,可能致使最多1秒的AOF記錄丟失。
  • no:Redis永不直接調用文件同步,而是讓操做系統來決定什麼時候同步磁盤。性能較好,但很不安全。

AOF默認是關閉的,須要在配置文件中手動開啓。

# 只有在「yes」下,aof重寫/文件同步等特性纔會生效  
appendonly yes  
## 指定aof文件名稱  
appendfilename appendonly.aof  
## 指定aof操做中文件同步策略,有三個合法值:always everysec no,默認爲everysec  
appendfsync everysec 
## 在aof-rewrite期間,appendfsync是否暫緩文件同步,"no"表示「不暫緩」,「yes」表示「暫緩」,默認爲「no」  
no-appendfsync-on-rewrite no  
## aof文件rewrite觸發的最小文件尺寸(mb,gb),只有大於此aof文件大於此尺寸是纔會觸發rewrite,默認「64mb」,建議「512mb」  
auto-aof-rewrite-min-size 64mb

Redis4.0混合持久化

Redis4.0提供了一種新的持久化機制,就是RDB和AOF結合使用,將rdb文件內容和aof文件存在一塊兒,AOF中保存的再也不是所有數據了,而是從RDB開始的到結束的增量日誌。

這樣在Redis恢復數據的時候,能夠先僞裝RDB文件中的內容,而後在順序執行AOF日誌中指令,這樣就將Redis重啓時恢復數據的效率獲得了大幅度提高。

結尾

恩,此次就先總結到這裏吧,後面會繼續總結Redis相關知識,LRU、LFU、內存淘汰策略,管道等等。

相關文章
相關標籤/搜索