佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > Redis為什么快如何實現(xiàn)高可用及持久化

Redis為什么快如何實現(xiàn)高可用及持久化

熱門標簽:服務外包 百度競價排名 AI電銷 呼叫中心市場需求 網(wǎng)站排名優(yōu)化 Linux服務器 鐵路電話系統(tǒng) 地方門戶網(wǎng)站

前言

作為Java程序員,在面試過程中,緩存相關的問題是躲不掉的,肯定會問,例如緩存一致性問題,緩存雪崩、擊穿、穿透等。說到緩存,那肯定少不了Redis,我在面試的時候也是被問了很多關于Redis相關的知識,但是Redis的功能太強大了,并不是一時半會兒能掌握好的,因為有些高級特性或是知識平時并不會用到。
所以回答的不好,人家就會覺得你對自己平時使用的工具都沒有了解,自然就涼涼了。其實很早就有這個打算,打算好好總結(jié)一下Redis的知識,但也是由于自己都沒有好好的了解Redis呢,所以一直沒有開始。這次準備慢慢的來總結(jié)。

Redis為什么這么快

Redis是一個由C語言編寫的開源的,基于內(nèi)存,支持多種數(shù)據(jù)結(jié)構(gòu)可持久化的NoSQL數(shù)據(jù)庫。
它速度快主要是有以下幾個原因:

  • 基于內(nèi)存運行,性能高效;
  • 數(shù)據(jù)結(jié)構(gòu)設計高效,例如String是由動態(tài)字符數(shù)組構(gòu)成,zset內(nèi)部的跳表;
  • 采用單線程,避免了線程的上下文切換,也避免了線程競爭產(chǎn)生的死鎖等問題;
  • 使用I/O多路復用模型,非阻塞IO;

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

高可用方案

雖然單臺Redis的的性能很好,但是Redis的單節(jié)點并不能保證它不會掛了啊,畢竟單節(jié)點的Redis是有上限的,而且人家單節(jié)點又要讀又要寫,小身板扛不住咋辦,所以為了保證高可用,一般都是做成集群。

主從(Master-Slave)

Redis官方是支持主從同步的,而且還支持從從同步,從從同步也可以理解為主從同步,只不過從從同步的主節(jié)點是另一個主從的從節(jié)點。

有了主從同步的集群,那么主節(jié)點就負責提供寫操作,而從節(jié)點就負責支持讀操作。

那么他們之間是如何進行數(shù)據(jù)同步的呢?

如果Slave(從節(jié)點)是第一次跟Master進行連接,

  • 那么會首先會向Master發(fā)送同步請求psync;
  • 主節(jié)點接收到同步請求,開始fork主子進程開始進行全量同步,然后生成RDB文件;
  • 這個時候主節(jié)點同時會將新的寫請求,保存到緩存區(qū)(buffer)中;
  • 從節(jié)點接收到RDB文件后,先清空老數(shù)據(jù),然后將RDB中數(shù)據(jù)加載到內(nèi)存中;
  • 等到從節(jié)點將RDB文件同步完成后再同步緩存區(qū)中的寫請求。

數(shù)組被占滿之后就會覆蓋掉最早之前的數(shù)據(jù)。

所以如果由于網(wǎng)絡或是其他原因,造成緩存區(qū)中的數(shù)據(jù)被覆蓋了,那么當從節(jié)點處理完主節(jié)點的RDB文件后,就不得不又要進行一全量的RDB文件的復制,才能保證主從節(jié)點的數(shù)據(jù)一致。

如果不設置好合理的buffer區(qū)空間,是會造成一個RDB復制的死循環(huán)。

當主從間的數(shù)據(jù)同步完成之后,后面主節(jié)點的每次寫操作就都會同步到從節(jié)點,這樣進行增量同步了。

由于負載的不斷上升就導致了主從之間的延時變大,所以就有了上面我說的從從同步了,主節(jié)點先同步到一部分從節(jié)點,然后由從節(jié)點去同步其他的從節(jié)點。

在Redis從2.8.18開始支持無盤復制,主節(jié)點通過套接字,一邊遍歷內(nèi)存中的數(shù)據(jù),一邊讓數(shù)據(jù)發(fā)送給從節(jié)點,從節(jié)點和之前一樣,先將數(shù)據(jù)存儲在磁盤文件中,然后再一次性加載。

另外由于主從同步是異步的,所以從Redis3.0之后出現(xiàn)了同步復制,就是通過wait命令來進行控制,wait命令有兩個參數(shù),第一個是從庫數(shù)量,第二個是等待從庫的復制時間,如果第二個參數(shù)設置為0,那么就是代表要等待所有從庫都復制完才去執(zhí)行后面的命令。
但是這樣就會存在一個隱患,當網(wǎng)絡異常后,wait命令會一直阻塞下去,導致Redis不可用。

哨兵(Sentinel)

哨兵可以監(jiān)控Redis集群的健康狀態(tài),當主節(jié)點掛掉之后,選舉出新的主節(jié)點??蛻舳嗽谑褂肦edis的時候會先通過Sentinel來獲取主節(jié)點地址,然后再通過主節(jié)點來進行數(shù)據(jù)交互。當主節(jié)點掛掉之后,客戶端會再次向Sentinel獲取主節(jié)點,這樣客戶端就可以無感知的繼續(xù)使用了。

哨兵集群工作過程,主節(jié)點掛掉之后會選舉出新的主節(jié)點,然后監(jiān)控掛掉的節(jié)點,當掛掉的節(jié)點恢復后,原先的主節(jié)點就會變成從節(jié)點,從新的主節(jié)點那里建立主從關系。

集群分片(Redis Cluster)

Redis Cluster是Redis官方推薦的集群模式,Redis Cluster將所有數(shù)據(jù)劃分到16384個槽(slots)中,每個節(jié)點負責一部分槽位的讀寫操作。

存儲

Redis Cluster默認是通過CRC16算法獲取到key的hash值,然后再對16384進行取余(CRC16(key)%16384),獲取到的槽位在哪個節(jié)點負責的范圍內(nèi)(這里一般是會有一個槽位和節(jié)點的映射表來進行快速定位節(jié)點的,通常使用bitmap來實現(xiàn)),就存儲在哪個節(jié)點上。

重定向

當Redis Cluster的客戶端在和集群建立連接的時候,也會獲得一份槽位和節(jié)點的配置關系(槽位和節(jié)點的映射表),這樣當客戶端要查找某個key時,可以直接定位到目標節(jié)點。

但是當客戶端發(fā)送請求時,如果接收請求的節(jié)點發(fā)現(xiàn)該數(shù)據(jù)的槽位并不在當前節(jié)點上,那么會返回MOVED指令將正確的槽位和節(jié)點信息返回給客戶端,客戶接著請求正確的節(jié)點獲取數(shù)據(jù)。

一般客戶端在接收到MOVED指令后,也會更新自己本地的槽位和節(jié)點的映射表,這樣下次獲取數(shù)據(jù)時就可以直接命中了。這整個重定向的過程對客戶端是透明的。

數(shù)據(jù)遷移

當集群中新增節(jié)點或刪除節(jié)點后,節(jié)點間的數(shù)據(jù)遷移是按槽位為單位的,一個槽位一個槽位的遷移,當遷移時原節(jié)點狀態(tài)處于:magrating,目標節(jié)點處于:importing。

在遷移過程中,客戶端首先訪問舊節(jié)點,如果數(shù)據(jù)還在舊節(jié)點,那么舊節(jié)點正常處理,如果不在舊節(jié)點,就會返回一個-ASK + 目標節(jié)點地址的指令,客戶端收到這個-ASK指令后,向目標節(jié)點執(zhí)行一個asking指令(告訴新節(jié)點,必須處理客戶端這個數(shù)據(jù)),然后再向目標節(jié)點執(zhí)行客戶端的訪問數(shù)據(jù)的指令。

容錯

Redis Cluster可以為每個主節(jié)點設置多個從節(jié)點,當單個主節(jié)點掛掉后,集群會自動將其中某個從節(jié)點提升為主節(jié)點,若沒有從節(jié)點,那么集群將處于不可用狀態(tài)。
Redis提供了一個參數(shù):cluster-require-full-coverage,用來配置可以允許部分節(jié)點出問題后,還有其他節(jié)點在運行時可以正常提供服務。

另外一點比較特殊的是,Cluster中當一個節(jié)點發(fā)現(xiàn)某個其他節(jié)點出現(xiàn)失聯(lián)了,這個時候問題節(jié)點只是PFailPossibly-可能下線),然后它會把這個失聯(lián)信息廣播給其他節(jié)點,當一個節(jié)點接收到某個節(jié)點的失聯(lián)信息達到集群的大多數(shù)時,就可以將失聯(lián)節(jié)點標記為下線,然后將下線信息廣播給其他節(jié)點。若失聯(lián)節(jié)點為主節(jié)點,那么將立即對該節(jié)點進行主從切換。

Redis高可用就先說到這里吧,后面其實還有Codis,但是目前Cluster逐漸流行起來了,Codis的競爭力逐漸被蠶食,而且對新版本的支持,更新的也比較慢,所以這里就不說它了,感興趣的可以自己去了解一下,國人開源的Redis集群模式Codis。

持久化

Redis持久化的意義在于,當出現(xiàn)宕機問題后,能將數(shù)據(jù)恢復到緩存中,它提供了兩種持久化機制:一種是快照(RDB),一種是AOF日志。

快照是一次全量備份,而AOF是增量備份??煺帐莾?nèi)存數(shù)據(jù)的二進制序列化形式,存儲上非常緊湊,而AOF日志記錄的是內(nèi)存數(shù)據(jù)修改的指令記錄文本。

快照備份(RDB)

因為Redis是單線程的,所以在做快照持久化的時候,通常有兩個選擇,save命令,會阻塞線程,直到備份完成;bgsave會異步的執(zhí)行備份,其實是fork出了一個子進程,用子進程去執(zhí)行快照持久化操作,將數(shù)據(jù)保存在一個.rdb文件中。

子進程剛剛產(chǎn)生的時候,是和父進程共享內(nèi)存中的數(shù)據(jù)的,但是子進程做持久化時,是不會修改數(shù)據(jù)的,而父進程是要持續(xù)提供服務的,所以父進程就會持續(xù)的修改內(nèi)存中的數(shù)據(jù),這個時候父進程就會將內(nèi)存中的數(shù)據(jù),Copy出一份來進行修改。

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

子進程由于數(shù)據(jù)沒有變化就會一直的去遍歷數(shù)據(jù),進程持久化操作了,這就是只保留了創(chuàng)建子進程的時候的快照。

那么RDB是在什么時候觸發(fā)的呢?

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

上這段配置就是在redis.conf文件中配置的,第一個參數(shù)是時間單位是秒,第二個參數(shù)執(zhí)行數(shù)據(jù)變化的次數(shù)。
意思就是說:300秒之內(nèi)至少發(fā)生10次寫操作、
60秒之內(nèi)發(fā)生至少10000次寫操作,只要滿足任一條件,均會觸發(fā)bgsave

增量日志備份(AOF)

Redis在接收到客戶端請求指令后,會先進行校驗,校驗成功后,立即將指令存儲到AOF日志文件中,就是說,Redis是先記錄日志,再執(zhí)行命令。這樣即使命令還沒執(zhí)行突然宕機了,通過AOF日志文件也是可以恢復的。

AOF重寫

AOF日志文件,隨著時間的推移,會越來越大,所以就需要進行重寫瘦身。AOF重寫的原理就是,fork一個子進程,對內(nèi)存進行遍歷,然后生成一系列的Redis指令,然后序列化到一個新的aof文件中。然后再將遍歷內(nèi)存階段的增量日志,追加到新的aof文件中,追加完成后立即替換舊的aof文件,這樣就完成了AOF的瘦身重寫。

fsync

因為AOF是一個寫文件的IO操作,是比較耗時。所以AOF日志并不是直接寫入到日志文件的,而是先寫到一個內(nèi)核的緩存中,然后通過異步刷臟,來將數(shù)據(jù)保存到磁盤的。

由于這個情況,就導致了會有還沒來得急刷臟然后就宕機了,導致數(shù)據(jù)丟失的風險。

所以Redis提供了一個配置,可以手動的來選擇刷臟的頻率。

  • always:每一條AOF記錄都立即同步到文件,性能很低,但較為安全。
  • everysec:每秒同步一次,性能和安全都比較中庸的方式,也是redis推薦的方式。如果遇到物理服務器故障,可能導致最多1秒的AOF記錄丟失。
  • no:Redis永不直接調(diào)用文件同步,而是讓操作系統(tǒng)來決定何時同步磁盤。性能較好,但很不安全。

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觸發(fā)的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才會觸發(fā)rewrite,默認“64mb”,建議“512mb” 
auto-aof-rewrite-min-size 64mb 

Redis4.0混合持久化

Redis4.0提供了一種新的持久化機制,就是RDB和AOF結(jié)合使用,將rdb文件內(nèi)容和aof文件存在一起,AOF中保存的不再是全部數(shù)據(jù)了,而是從RDB開始的到結(jié)束的增量日志。

這樣在Redis恢復數(shù)據(jù)的時候,可以先假裝RDB文件中的內(nèi)容,然后在順序執(zhí)行AOF日志中指令,這樣就將Redis重啟時恢復數(shù)據(jù)的效率得到了大幅度提升。

結(jié)尾

恩,這次就先總結(jié)到這里吧,后面會繼續(xù)總結(jié)Redis相關知識,LRU、LFU、內(nèi)存淘汰策略,管道等等。

到此這篇關于Redis為什么快如何實現(xiàn)高可用及持久化的文章就介紹到這了,更多相關redis持久化高可用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • Redis不是一直號稱單線程效率也很高嗎,為什么又采用多線程了?
  • redis單線程快的原因和原理
  • Redis憑啥可以這么快
  • 為啥Redis使用pipelining會更快
  • Redis高效率原因及數(shù)據(jù)結(jié)構(gòu)分析

標簽:湖南 黃山 銅川 蘭州 崇左 衡水 湘潭 仙桃

巨人網(wǎng)絡通訊聲明:本文標題《Redis為什么快如何實現(xiàn)高可用及持久化》,本文關鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡,涉及言論、版權(quán)與本站無關。
  • 相關文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    浦北县| 万盛区| 龙南县| 云阳县| 武汉市| 莎车县| 沛县| 尚志市| 上蔡县| 泽州县| 昌黎县| 社会| 乌苏市| 安义县| 洪洞县| 库伦旗| 临沧市| 曲麻莱县| 改则县| 梨树县| 防城港市| 平塘县| 乐山市| 英德市| 桐柏县| 进贤县| 白河县| 建瓯市| 喀喇| 虹口区| 德昌县| 蓝山县| 绥江县| 金华市| 灵山县| 普宁市| 新乡市| 阳谷县| 墨江| 南开区| 呼玛县|