Java八股文——Redis與一致性協議


Redis

  • Redis數據結構

  String字符串,list鏈表,hash鍵值對,set集合,sortedset有序集合,BloomFilter布隆過濾器

  布隆過濾器原理:當一個元素被加入到集合中時,通過K個散列函數將元素分布到一個位數組上的K個點,查詢該元素的時候,如果hash出來的這個K個點都為1,則說明元素可能存在,如果有一個為0,則說明元素一定不存在。優點:可用於防止緩存穿透,數據庫的元素先散列到布隆數組中,查詢時先通過布隆過濾器判斷是否存在再查詢。缺點:犧牲了准確性和便利性,查詢出來位數組上的點全為1也不一定存在。刪除時也不能修改位數組,否則容易影響其他數據的查詢

  • Redis數據結構的底層實現

    1. String:
      struct sdshdr{ 
        //記錄buf數組中已使用字節的數量 
        //等於 SDS 保存字符串的長度 
        int len; 
        //記錄 buf 數組中未使用字節的數量
        int free//字節數組,用於保存字符串 
        char buf[];
      }

      優點:不會有字符串變更造成的內存溢出問題;獲取字符串長度的時間復雜度為1;空間預分配,惰性空間釋放free字段,會默認留一定的空間放置多次重分配內存

    2. Hash:數組+鏈表的基礎上,進行了一些rehash的優化,
      1. Redis的hash采用鏈地址法來處理沖突,沒有使用紅黑樹
      2. hash表節點采用單鏈表結構
      3. rehash優化:分治思想,將龐大的遷移工作量分到每一次curd中,避免服務繁忙
    3. List:雙向鏈表
    4. set:內部實現是value為null的Hashmap,通過計算hash的方式來排重。
    5. zset:使用hashmap和跳躍表(skiplist)來保證數據的存儲和有序,Hashmap存放的是成員到score的映射,跳躍表中存放的是所有成員,排序依據是score,使用跳躍表機構可以獲得比較高的查詢效率,並且實現上簡單。跳躍表:每個節點維持多個指向其他節點的指針,從而達到快速訪問節點的目的
  • Redis有事務嗎?

    1. multi開啟事務
    2. exec執行事務內命令
    3. discard取消事務
    4. watch監視一個或多個key,key被改動時事務將會被打斷

 

  • Redis單線程為什么執行這么快

    1. 純內存操作,讀寫的時候不會受到磁盤IO的限制
    2. 單線程操作,避免了切換上下文和競爭條件,也避免了多進程或線程切換的CPU損耗,也不存在死鎖的問題
    3. 采用了IO多路復用機制

 

  • 緩存問題:雪崩,穿透,擊穿

  雪崩:同一時間內大量的緩存失效導致DB壓力過大。
     這種情況可以對緩存的key在默認值上加上一些隨機時間,讓緩存失效的時間分散開來

  穿透:指緩存和數據庫中都沒有的數據,而用戶不斷發起請求
     這種情況可以對一些查詢出來不存在的數據緩存一個null,失效時間設置短一些防止數據庫新增了這個元素
     還可以使用布隆過濾器,在查詢之前先通過布隆過濾器判斷是否存在

  擊穿:這個和穿透有點像,是指某個熱點key在大量訪問的時候突然失效了,導致請求直接來到數據庫。
     熱點key設置為永不過期,或者定時對這個key進行更新

  • Redis出現熱點key造成集群訪問量傾斜的解決辦法?

    1. 使用本地緩存
    2. 利用分片算法的特性,對key進行打散處理,給hotkey加上前綴或后綴,把一個hotkey的數量變成redis實例個數的倍數
  • Redis里面有1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,如何將它們全部找出來

  redis中有個指令是keys,可以指定模式查詢對應的key列表

  ----如果這個redis正在給線上的業務提供服務,那使用keys指令會有什么問題?

  redis是單線程的,keys指令會阻塞一段時間,查詢完成才能恢復。可以使用scan指令替代,scan是無阻塞的指令,不過scan可能會查出重復的數據,這個可以在客戶端做一些去重。

  • 有使用過redis做異步隊列嗎?

  redis的list就可以做消息隊列,通過rpush插入,lpop彈出。

  ----能否生產一次,消費多次?

  redis有pub/sub模型,可以實現一對多的消費,不過在消費者掉線的時候會有消息丟失的問題,這類情況還是使用專業的消息中間件比較好

  ----那有用過redis做延時隊列嗎?

  可以使用sortedset,拿時間戳作為score,消息內容作為key調用zadd來生產消息,消費者用zrangebyscore指令獲取N秒之前的數據輪詢進行處理。

  • redis怎么做持久化的?

  redis有兩種持久化的方式,AOF和RDB,其中AOF是增量持久化,RDB是全量持久化,所有一般RDB會比較耗時,需要配合AOF使用,一般redis重啟時,會通過RDB文件重新構建內存,然后再使用AOF重新對近期數據做處理進行完整恢復,不過一般redis在AOF開啟時,是優先加載AOF,AOF文件找不到才使用RDB,AOF是日志型文件,每個指令都會存儲,而RDB會優化指令,保證數據完整即可。

  • 機器突然斷電,丟失的數據怎么辦?

  開啟了AOF之后,取決於AOF的sync設置,如果設置1s一次的話,會丟失1s內的數據,也可以設置成每條指令都sync一下,不過會導致磁盤io增多性能降低。

  • RDB的原理?

  redis在開啟RDB時,會定時fork一個子進程,子進程會根據父進程內存生成臨時快照文件,完成后對RDB文件進行替換。

  RDB相當於是redis某個時間段的內存快照,是十分緊湊的二進制文件,加載RDB文件比AOF文件快

  AOF相當於對redis的操作指令做復制,所有的寫入命令會追加到aof_buf(緩沖區)中,根據對應的策略向硬盤做同步操作,然后定期對AOF文件進行重寫,此外,AOF會對命令進行優化,簡化一些操作命令

  • redis集群的同步原理?

  redis的主從同步分為全同步和修改同步,全同步一般是在啟動的時候通過主機的bgsave生成RDB文件,從機通過RDB文件進行恢復,后續的同步都是修改同步,修改同步是通過propagate函數完成將一個操作記錄到aof文件中或者擴散到其他slave中,在該函數中通過調用feedAppendOnlyFile()將操作記錄到aof中,通過調用replicationFeedSlaves()將操作擴散到各slave中

  • redis集群模式的性能優化該怎么做?

    1. Master不做任何持久化操作
    2. 數據如果比較重要,某個slave開啟aof備份,策略為每秒一次
    3. master和slave在同一個局域網內
    4. 避免在壓力很大的主庫上增加從庫
  • redis怎么保證高可用

  redis中有哨兵模式,在主機宕機的時候,通過哨兵選舉出新的master,哨兵必須存在三個以上,否則無法選舉

    1. 領導者選舉

    作用:選舉出一個sentenel節點作為領導者去進行故障轉移操作。

    過程:

      1). 每個做主觀下線的sentinel節點向其他sentinel節點發送vote命令,要求將它設置為領導者。
      2). 收到命令的sentinel節點如果還沒有同意過其他的sentinel發送的命令(還未投過票),那么就會同意,否則拒絕。
      3). 如果該sentinel節點發現自己的票數已經過半且達到了quorum的值,就會成為領導者。
      4). 如果這個過程出現多個sentinel成為領導者,則會等待一段時間重新選舉。

    2. 選出新的master節點

      redis sentinel會選一個合適的slave來升級為master,那么,如何選擇一個合適的slave呢?順序如下:

      1). 選擇slave-priority最高的slave節點(默認是相同)。
      2). 選擇復制偏移量最大的節點。
      3). 如果以上兩個條件都不滿足,選runId最小的(啟動最早的)。

    3. 更改slave節點的master節點

      當選舉出新的master節點后,會將其余的節點變更為新的master節點的slave節點,如果原有的master節點重新上線,成為新的master節點的slave節點

  • redis的哨兵sentinel怎么保證自身的高可用呢?使用了什么協議?

  使用了Raft協議來保證高可用
  Raft協議中的相關概念:
  1、角色定義
    Leader:
    主節點,整個集群只有一個Leader,所有的寫請求都通過Leader發送給Follower;
    Follower:
    從節點(服從角色);
    Candidate:
    在Leader消息發送失敗或宕機,整集群沒有Leader時,此時Follower接收Leader的心跳包失敗,則Follwer開始競選Leader時,它們的身份是Candidate。Candidate只是個中間狀態,不會長期存在。
  2、Term(任期)定義
    在每一個Leader的任期期間,都有唯一表示該任期的一個Term;
  3、基本操作
    1、Raft將state machine replication划分為三個子問題
      1、Leader Election
      2、Log Replication
      3、Safety
    2、Leader Election步驟
      集群啟動或Leader的心跳包消息無法發送給Follower時,觸發 Leader Election——選主 操作。
    3、Log Replication步驟
      1、所有的寫請求都要經過Leader;
      2、Leader將寫請求攜帶在心跳包中發送給Follower;
      3、當Leader收到多數派回復的消息后,則先自己提交寫操作,同時發送Commit請求給Follower;
    4、Safety保證
      1、Leader宕機感知:
        a、Raft通過TimeOut來保證Follower能正確感知Leader宕機或消息丟失的事件,並觸發Follower競選Leader;
        b、Leader需要給Follower發送心跳包(heartbeats),數據也是攜帶在心跳包中發送給Follower的;
      2、選主平票情況
        Leader Election時平票情況下,則兩個Candidates會產生一個隨機的Timewait,繼續發送下一個競選消息。
      3、腦裂(大小集群)情況:
        小集群由於沒有得到多數派的回復,寫操作失敗;
        大集群會發生重新選主的過程,且新Leader擁有自己新的Term(任期),寫操作成功;
        當小集群回到大集群時,由於小集群的Term小於新集群的Term,則同步新集群的信息。

  • 有沒有考慮過,如果多個系統同時操作(並發)Redis帶來的數據問題

    可以基於分布式鎖操作。每個系統通過 Redis獲取分布式鎖,確保同一時間,只能有一個系統實例在操作某個 Key,別人都不允許讀和寫。
  你要寫入緩存的數據,都是從 MySQL 里查出來的,都得寫入 MySQL 中,寫入 MySQL 中的時候必須保存一個時間戳,從 MySQL 查出來的時候,時間戳也查出來。
  每次要寫之前,先判斷一下當前這個 Value 的時間戳是否比緩存里的 Value 的時間戳要新。如果是的話,那么可以寫,否則,就不能用舊的數據覆蓋新的數據。

  • 如何保證緩存與數據庫的雙寫一致性?

  先更新數據庫,再刪除緩存,將刪除緩存的操作放在更新數據庫的同一個事務中,如果刪除緩存失敗,拋出異常使當前事務回滾

  • redis線程模型了解嗎?

    Redis 基於 Reactor 模式開發了自己的網絡事件處理器: 這個處理器被稱為文件事件處理器(file event handler)。文件事件處理器使用 I/O 多路復用(multiplexing)程序來同時監聽多個套接字, 並根據套接字目前執行的任務來為套接字關聯不同的事件處理器。文件處理器包括連接處理器,命令請求處理器,命令回復處理器,文件事件處理器以單線程方式運行, 但通過使用 I/O 多路復用程序來監聽多個套接字, 文件事件處理器既實現了高性能的網絡通信模型, 又可以很好地與 redis 服務器中其他同樣以單線程方式運行的模塊進行對接, 這保持了 Redis 內部單線程設計的簡單性

  • 如何實現分布式鎖?

  兩種實現方式,redis和zookeeper,其中redis是通過lua表達式實現

  • 說到zookeeper,了解ZAB協議嗎?

  zookeeper依賴ZAB協議來實現分布式數據一致性,ZAB協議全稱為原子消息廣播協議,ZAB協議可以說是在Paxos算法基礎上進行了擴展和改造的。

  在ZooKeeper中所有的事務請求都由一個主服務器也就是Leader來處理,其他服務器為Follower,Leader將客戶端的事務請求轉換為事務Proposal,並且將Proposal分發給集群中其他所有的Follower,然  后Leader等待Follwer反饋,當有 過半數(>=N/2+1) 的Follower反饋信息后,Leader將再次向集群內Follower廣播Commit信息,Commit為將之前的Proposal提交;

  ZAB協議中節點存在三種狀態:

    Looking :系統剛啟動時或者Leader崩潰后正處於選舉狀態
    Following :Follower節點所處的狀態,Follower與Leader處於數據同步階段;
    Leading :Leader所處狀態,當前集群中有一個Leader為主進程;

  Zookeeper剛啟動時,所有節點都處於Looking狀態,這時集群會選舉出一個Leader,Leader會處於Leading狀態,其余節點發現存在主節點后,處於Following狀態,並且與Leader保持同步,當從節點發現  Leader失去聯系后,會開始新一輪選舉。Zookeeper整個生命周期內都處於這樣的循環

  ZAB協議定義了選舉(election)、發現(discovery)、同步(sync)、廣播(Broadcast)四個階段。

  選舉時,節點都會發送(myID,ZXID)到其余節點,通過判斷ZXID(事務ID)的大小來決定哪個節點為leader,如果有多個節點都有LastZXID,則由myId決定,id小的為leader,id小表明該節點先加入集群。當投票數過半時,leader就產生了。然后Leader會以自身的lastZXID為基准,將follower同步。

  客戶端提交事務請求時Leader節點為每一個請求生成一個事務Proposal,將其發送給集群中所有的Follower節點,收到過半Follower的反饋后開始對事務進行提交,ZAB協議使用了原子廣播協議;在ZAB協議中只需要得到過半的Follower節點反饋Ack就可以對事務進行提交,這也導致了Leader幾點崩潰后可能會出現數據不一致的情況,ZAB使用了崩潰恢復來處理數字不一致問題;消息廣播使用了TCP協議進行通訊所有保證了接受和發送事務的順序性。廣播消息時Leader節點為每個事務Proposal分配一個全局遞增的ZXID(事務ID),每個事務Proposal都按照ZXID順序來處理;

  Leader節點為每一個Follower節點分配一個隊列按事務ZXID順序放入到隊列中,且根據隊列的規則FIFO來進行事務的發送。Follower節點收到事務Proposal后會將該事務以事務日志方式寫入到本地磁盤中,成功后反饋Ack消息給Leader節點,Leader在接收到過半Follower節點的Ack反饋后就會進行事務的提交,以此同時向所有的Follower節點廣播Commit消息,Follower節點收到Commit后開始對事務進行提交;

  • 一致性算法除了ZAB還有哪些?

  Base-Paxos

    paxos——Chubby(Google首次運用Multi Paxos算法到工程領域)

  Multi-Paxos

    zab協議——Zookeeper,raft協議——redis哨兵

  Paxos中的角色分類

  • Proposer 提案者:可以有多個,用於發起一個議案(由client向Proposer發出),議案包括議案編號、議案內容
  • Acceptor 接收者、批准者:用於接收Proposer提出的議案,並決定采納哪一個議案
  • Learner 學習者、記錄者:在最終決定采納某個議案后,進行記錄,不做其他事

  其中Proposer在Base-Paxos中可以存在多個,而Multi-Paxos中僅有一個,別名leader。因此,Base-paxos存在活鎖問題:

  

  三種算法的區別:

  Raft:

    追加日志的操作必須是連續的(Multi-Paxos中是並發)
    只有擁有最新、最全日志的節點才能夠當選Leader(Multi-Paxos中任意節點都可以寫日志,無限制)

  zab:

    Raft每一次leader的任期叫做term,ZAB中叫做epoch
    Raft由leader向follower發送心跳,ZAB相反
    選主投票方式不同
    事務操作方式不同

  • 一致性就代表了正確性嗎?

  一致性並不代表完全正確性,一致性需要客戶端和共識算法(Consensus)來共同保證。
  Client Request操作的三個可能結果:成功、失敗、unknown(Timeout)
  理解unknown(Timeout)
  場景:Client寫請求,Leader向Follower同步日志,此時集群中有3個節點失敗,2個節點存活,結果是? 假設節點為:S1、S2、S3、S4、S5(Leader)
  假設S5和S4存活,Client發起 第N次寫請求 為 操作I 時,由於Leader沒有得到多數派的回復,操作I只被發送到了S4中,此時Leader即會返回Client unknown,因為Leader不知道后面會不會成功將該條日志寫入多數派中。
  結果1:假設Leader在返回客戶端后,宕機的Follower:S1、S2、S3恢復正常,Leader再次發送 第N次寫請求——操作I,且得到了多數派的回復,則提交日志,寫操作最終結果為成功;
  結果2:假設Leader在返回客戶端后,此時S5和S4宕機,且S1、S2、S3恢復正常,此時S1、S2、S3觸發選主操作,且集群恢復可用,如果此時Client發起 第N+1次請求 為 操作I+1 ,且Client操作成功后 S5、S4恢復正常,則保存在S5、S4中的 操作I 會被刪除,S5、S4同步最新的 操作I+1 到本地。則 第N次寫請求—操作I 失敗;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM