Redis你不得不探索的11個問題

?

1. 說說Redis基本數據類型有哪些吧

  1. 字符串:redis沒有直接使用C語言傳統的字符串表示,而是自己實現的叫做簡單動態字符串SDS的抽象類型。C語言的字符串不記錄自身的長度信息,而SDS則保存了長度信息,這樣將獲取字符串長度的時間由O(N)降低到了O(1),同時可以避免緩沖區溢出和減少修改字符串長度時所需的內存重分配次數。

  2. 鏈表linkedlist:redis鏈表是一個雙向無環鏈表結構,很多發布訂閱、慢查詢、監視器功能都是使用到了鏈表來實現,每個鏈表的節點由一個listNode結構來表示,每個節點都有指向前置節點和后置節點的指針,同時表頭節點的前置和后置節點都指向NULL。

  3. 字典hashtable:用于保存鍵值對的抽象數據結構。redis使用hash表作為底層實現,每個字典帶有兩個hash表,供平時使用和rehash時使用,hash表使用鏈地址法來解決鍵沖突,被分配到同一個索引位置的多個鍵值對會形成一個單向鏈表,在對hash表進行擴容或者縮容的時候,為了服務的可用性,rehash的過程不是一次性完成的,而是漸進式的。

  4. 跳躍表skiplist:跳躍表是有序集合的底層實現之一,redis中在實現有序集合鍵和集群節點的內部結構中都是用到了跳躍表。redis跳躍表由zskiplist和zskiplistNode組成,zskiplist用于保存跳躍表信息(表頭、表尾節點、長度等),zskiplistNode用于表示表跳躍節點,每個跳躍表的層高都是1-32的隨機數,在同一個跳躍表中,多個節點可以包含相同的分值,但是每個節點的成員對象必須是唯一的,節點按照分值大小排序,如果分值相同,則按照成員對象的大小排序。

  5. 整數集合intset:用于保存整數值的集合抽象數據結構,不會出現重復元素,底層實現為數組。

  6. 壓縮列表ziplist:壓縮列表是為節約內存而開發的順序性數據結構,他可以包含多個節點,每個節點可以保存一個字節數組或者整數值。

基于這些基礎的數據結構,redis封裝了自己的對象系統,包含字符串對象string、列表對象list、哈希對象hash、集合對象set、有序集合對象zset,每種對象都用到了至少一種基礎的數據結構。

redis通過encoding屬性設置對象的編碼形式來提升靈活性和效率,基于不同的場景redis會自動做出優化。不同對象的編碼如下:

  1. 字符串對象string:int整數、embstr編碼的簡單動態字符串、raw簡單動態字符串

  2. 列表對象list:ziplist、linkedlist

  3. 哈希對象hash:ziplist、hashtable

  4. 集合對象set:intset、hashtable

  5. 有序集合對象zset:ziplist、skiplist

2. Redis為什么快呢?

redis的速度非常的快,單機的redis就可以支撐每秒10幾萬的并發,相對于mysql來說,性能是mysql的幾十倍。速度快的原因主要有幾點:

  1. 完全基于內存操作

  2. C語言實現,優化過的數據結構,基于幾種基礎的數據結構,redis做了大量的優化,性能極高

  3. 使用單線程,無上下文的切換成本

  4. 基于非阻塞的IO多路復用機制

3. 那為什么Redis6.0之后又改用多線程呢?

redis使用多線程并非是完全摒棄單線程,redis還是使用單線程模型來處理客戶端的請求,只是使用多線程來處理數據的讀寫和協議解析,執行命令還是使用單線程。

這樣做的目的是因為redis的性能瓶頸在于網絡IO而非CPU,使用多線程能提升IO讀寫的效率,從而整體提高redis的性能。

4. 知道什么是熱key嗎?熱key問題怎么解決?

所謂熱key問題就是,突然有幾十萬的請求去訪問redis上的某個特定key,那么這樣會造成流量過于集中,達到物理網卡上限,從而導致這臺redis的服務器宕機引發雪崩。

針對熱key的解決方案:

  1. 提前把熱key打散到不同的服務器,降低壓力

  2. 加入二級緩存,提前加載熱key數據到內存中,如果redis宕機,走內存查詢

5. 什么是緩存擊穿、緩存穿透、緩存雪崩?

緩存擊穿

緩存擊穿的概念就是單個key并發訪問過高,過期時導致所有請求直接打到db上,這個和熱key的問題比較類似,只是說的點在于過期導致請求全部打到DB上而已。

解決方案:

  1. 加鎖更新,比如請求查詢A,發現緩存中沒有,對A這個key加鎖,同時去數據庫查詢數據,寫入緩存,再返回給用戶,這樣后面的請求就可以從緩存中拿到數據了。

  2. 將過期時間組合寫在value中,通過異步的方式不斷的刷新過期時間,防止此類現象。

緩存穿透

緩存穿透是指查詢不存在緩存中的數據,每次請求都會打到DB,就像緩存不存在一樣。

針對這個問題,加一層布隆過濾器。布隆過濾器的原理是在你存入數據的時候,會通過散列函數將它映射為一個位數組中的K個點,同時把他們置為1。

這樣當用戶再次來查詢A,而A在布隆過濾器值為0,直接返回,就不會產生擊穿請求打到DB了。

顯然,使用布隆過濾器之后會有一個問題就是誤判,因為它本身是一個數組,可能會有多個值落到同一個位置,那么理論上來說只要我們的數組長度夠長,誤判的概率就會越低,這種問題就根據實際情況來就好了。

?

緩存雪崩

當某一時刻發生大規模的緩存失效的情況,比如你的緩存服務宕機了,會有大量的請求進來直接打到DB上,這樣可能導致整個系統的崩潰,稱為雪崩。雪崩和擊穿、熱key的問題不太一樣的是,他是指大規模的緩存都過期失效了。

針對雪崩幾個解決方案:

  1. 針對不同key設置不同的過期時間,避免同時過期

  2. 限流,如果redis宕機,可以限流,避免同時刻大量請求打崩DB

  3. 二級緩存,同熱key的方案。

6. Redis的過期策略有哪些?

redis主要有2種過期刪除策略

惰性刪除

惰性刪除指的是當我們查詢key的時候才對key進行檢測,如果已經達到過期時間,則刪除。顯然,他有一個缺點就是如果這些過期的key沒有被訪問,那么他就一直無法被刪除,而且一直占用內存。

?

定期刪除

定期刪除指的是redis每隔一段時間對數據庫做一次檢查,刪除里面的過期key。由于不可能對所有key去做輪詢來刪除,所以redis會每次隨機取一些key去做檢查和刪除。

7. 那么定期+惰性都沒有刪除過期的key怎么辦?

假設redis每次定期隨機查詢key的時候沒有刪掉,這些key也沒有做查詢的話,就會導致這些key一直保存在redis里面無法被刪除,這時候就會走到redis的內存淘汰機制。

  1. volatile-lru:從已設置過期時間的key中,移出最近最少使用的key進行淘汰

  2. volatile-ttl:從已設置過期時間的key中,移出將要過期的key

  3. volatile-random:從已設置過期時間的key中隨機選擇key淘汰

  4. allkeys-lru:從key中選擇最近最少使用的進行淘汰

  5. allkeys-random:從key中隨機選擇key進行淘汰

  6. noeviction:當內存達到閾值的時候,新寫入操作報錯

8. 持久化方式有哪些?有什么區別?

redis持久化方案分為RDB和AOF兩種。

RDB

RDB持久化可以手動執行也可以根據配置定期執行,它的作用是將某個時間點上的數據庫狀態保存到RDB文件中,RDB文件是一個壓縮的二進制文件,通過它可以還原某個時刻數據庫的狀態。由于RDB文件是保存在硬盤上的,所以即使redis崩潰或者退出,只要RDB文件存在,就可以用它來恢復還原數據庫的狀態。

可以通過SAVE或者BGSAVE來生成RDB文件。

SAVE命令會阻塞redis進程,直到RDB文件生成完畢,在進程阻塞期間,redis不能處理任何命令請求,這顯然是不合適的。

BGSAVE則是會fork出一個子進程,然后由子進程去負責生成RDB文件,父進程還可以繼續處理命令請求,不會阻塞進程。

AOF

AOF和RDB不同,AOF是通過保存redis服務器所執行的寫命令來記錄數據庫狀態的。

AOF通過追加、寫入、同步三個步驟來實現持久化機制。

  1. 當AOF持久化處于激活狀態,服務器執行完寫命令之后,寫命令將會被追加append到aof_buf緩沖區的末尾

  2. 在服務器每結束一個事件循環之前,將會調用flushAppendOnlyFile函數決定是否要將aof_buf的內容保存到AOF文件中,可以通過配置appendfsync來決定。

 

always?##aof_buf內容寫入并同步到AOF文件
everysec?##將aof_buf中內容寫入到AOF文件,如果上次同步AOF文件時間距離現在超過1秒,則再次對AOF文件進行同步
no?##將aof_buf內容寫入AOF文件,但是并不對AOF文件進行同步,同步時間由操作系統決定

如果不設置,默認選項將會是everysec,因為always來說雖然最安全(只會丟失一次事件循環的寫命令),但是性能較差,而everysec模式只不過會可能丟失1秒鐘的數據,而no模式的效率和everysec相仿,但是會丟失上次同步AOF文件之后的所有寫命令數據。

9. 怎么實現Redis的高可用?

要想實現高可用,一臺機器肯定是不夠的,而redis要保證高可用,有2個可選方案。

主從架構

主從模式是最簡單的實現高可用的方案,核心就是主從同步。主從同步的原理如下:

  1. slave發送sync命令到master

  2. master收到sync之后,執行bgsave,生成RDB全量文件

  3. master把slave的寫命令記錄到緩存

  4. bgsave執行完畢之后,發送RDB文件到slave,slave執行

  5. master發送緩存中的寫命令到slave,slave執行

這里我寫的這個命令是sync,但是在redis2.8版本之后已經使用psync來替代sync了,原因是sync命令非常消耗系統資源,而psync的效率更高。

哨兵

基于主從方案的缺點還是很明顯的,假設master宕機,那么就不能寫入數據,那么slave也就失去了作用,整個架構就不可用了,除非你手動切換,主要原因就是因為沒有自動故障轉移機制。而哨兵(sentinel)的功能比單純的主從架構全面的多了,它具備自動故障轉移、集群監控、消息通知等功能。

?

哨兵可以同時監視多個主從服務器,并且在被監視的master下線時,自動將某個slave提升為master,然后由新的master繼續接收命令。整個過程如下:

  1. 初始化sentinel,將普通的redis代碼替換成sentinel專用代碼

  2. 初始化masters字典和服務器信息,服務器信息主要保存ip:port,并記錄實例的地址和ID

  3. 創建和master的兩個連接,命令連接和訂閱連接,并且訂閱sentinel:hello頻道

  4. 每隔10秒向master發送info命令,獲取master和它下面所有slave的當前信息

  5. 當發現master有新的slave之后,sentinel和新的slave同樣建立兩個連接,同時每個10秒發送info命令,更新master信息

  6. sentinel每隔1秒向所有服務器發送ping命令,如果某臺服務器在配置的響應時間內連續返回無效回復,將會被標記為下線狀態

  7. 選舉出領頭sentinel,領頭sentinel需要半數以上的sentinel同意

  8. 領頭sentinel從已下線的的master所有slave中挑選一個,將其轉換為master

  9. 讓所有的slave改為從新的master復制數據

  10. 將原來的master設置為新的master的從服務器,當原來master重新回復連接時,就變成了新master的從服務器

sentinel會每隔1秒向所有實例(包括主從服務器和其他sentinel)發送ping命令,并且根據回復判斷是否已經下線,這種方式叫做主觀下線。當判斷為主觀下線時,就會向其他監視的sentinel詢問,如果超過半數的投票認為已經是下線狀態,則會標記為客觀下線狀態,同時觸發故障轉移。

10. 能說說redis集群的原理嗎?

如果說依靠哨兵可以實現redis的高可用,如果還想在支持高并發同時容納海量的數據,那就需要redis集群。redis集群是redis提供的分布式數據存儲方案,集群通過數據分片sharding來進行數據的共享,同時提供復制和故障轉移的功能。

節點

一個redis集群由多個節點node組成,而多個node之間通過cluster meet命令來進行連接,節點的握手過程:

  1. 節點A收到客戶端的cluster meet命令

  2. A根據收到的IP地址和端口號,向B發送一條meet消息

  3. 節點B收到meet消息返回pong

  4. A知道B收到了meet消息,返回一條ping消息,握手成功

  5. 最后,節點A將會通過gossip協議把節點B的信息傳播給集群中的其他節點,其他節點也將和B進行握手

?

槽slot

redis通過集群分片的形式來保存數據,整個集群數據庫被分為16384個slot,集群中的每個節點可以處理0-16384個slot,當數據庫16384個slot都有節點在處理時,集群處于上線狀態,反之只要有一個slot沒有得到處理都會處理下線狀態。通過cluster addslots命令可以將slot指派給對應節點處理。

slot是一個位數組,數組的長度是16384/8=2048,而數組的每一位用1表示被節點處理,0表示不處理,如圖所示的話表示A節點處理0-7的slot。

當客戶端向節點發送命令,如果剛好找到slot屬于當前節點,那么節點就執行命令,反之,則會返回一個MOVED命令到客戶端指引客戶端轉向正確的節點。(MOVED過程是自動的)

如果增加或者移出節點,對于slot的重新分配也是非常方便的,redis提供了工具幫助實現slot的遷移,整個過程是完全在線的,不需要停止服務。

故障轉移

如果節點A向節點B發送ping消息,節點B沒有在規定的時間內響應pong,那么節點A會標記節點B為pfail疑似下線狀態,同時把B的狀態通過消息的形式發送給其他節點,如果超過半數以上的節點都標記B為pfail狀態,B就會被標記為fail下線狀態,此時將會發生故障轉移,優先從復制數據較多的從節點選擇一個成為主節點,并且接管下線節點的slot,整個過程和哨兵非常類似,都是基于Raft協議做選舉。

11. 了解Redis事務機制嗎?

redis通過MULTI、EXEC、WATCH等命令來實現事務機制,事務執行過程將一系列多個命令按照順序一次性執行,并且在執行期間,事務不會被中斷,也不會去執行客戶端的其他請求,直到所有命令執行完畢。事務的執行過程如下:

  1. 服務端收到客戶端請求,事務以MULTI開始

  2. 如果客戶端正處于事務狀態,則會把事務放入隊列同時返回給客戶端QUEUED,反之則直接執行這個命令

  3. 當收到客戶端EXEC命令時,WATCH命令監視整個事務中的key是否有被修改,如果有則返回空回復到客戶端表示失敗,否則redis會遍歷整個事務隊列,執行隊列中保存的所有命令,最后返回結果給客戶端

WATCH的機制本身是一個CAS的機制,被監視的key會被保存到一個鏈表中,如果某個key被修改,那么REDIS_DIRTY_CAS標志將會被打開,這時服務器會拒絕執行事務。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/444592.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/444592.shtml
英文地址,請注明出處:http://en.pswp.cn/news/444592.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(一)深入淺出TCPIP之理解TCP報文格式和交互流程

目錄 1.引入TCP: 1.1 TCP用戶代碼 2. TCP數據報文格式 3 TCP棧及socket的初始化

leetcode85. 最大矩形

給定一個僅包含 0 和 1 的二維二進制矩陣,找出只包含 1 的最大矩形,并返回其面積。 示例: 輸入: [ ["1","0","1","0","0"], ["1","0","1","1","…

(二)深入淺出TCPIP之再識TCP,理解TCP三次握手(上)

目錄 1.三次握手 1.1 三次握手過程 1.2 TCP連接狀態 1.3 TCP狀態遷移路線分析 1.4 查看TCP狀態命令

(三)深入淺出TCPIP之再識TCP,理解TCP四次揮手(上)

目錄 1.TCP四次揮手過程 2.揮手連環發問 專欄其他文章: 理論篇: (一)深入淺出TCPIP之理解TCP報文格式和交互流程 (二)深入淺出TCPIP之再識TCP,理解TCP三次握手(上) (三)深入淺出TCPIP之再識TCP,理解TCP四次揮手(上) (四)深入淺出TCPIP之TCP三次握手和四次揮手…

(四)深入淺出TCPIP之TCP三次握手和四次揮手(下)的抓包分析

目錄 1. 通過netstat來分析服務器和客戶端的TCP狀態 2.通過tcpdump抓包分析服務器和客戶端的TCP狀態 2.1 語法

(六)深入淺出TCPIP之TCP擁塞控制

目錄 什么是網絡擁塞 如何避免擁塞 擁塞點 避免擁塞 慢啟動算法 算法思想

(五)深入淺出TCPIP之TCP流量控制

目錄 TCP流量控制 滑動窗口 固定窗口和滑動窗口 如何告知發送方窗口大小 滑動窗口細節

(八)深入淺出TCPIP之TCP長連接與短連接詳解

目錄 通信方式 連接方式 1.長連接 1.1服務器中的長連接 1.2 長連接的維護

(十)深入淺出TCPIP之網絡阻塞和非阻塞

專欄其他文章: 理論篇: (一)深入淺出TCPIP之理解TCP報文格式和交互流程 (二)深入淺出TCPIP之再識TCP,理解TCP三次握手(上) (三)深入淺出TCPIP之再識TCP,理解TCP四次揮手(上) (四)深入淺出TCPIP之TCP三次握手和四次揮手(下)的抓包分析 (五)深入淺出TCPIP之TCP流…

(九)深入淺出TCPIP之網絡同步異步

目錄 專欄其他文章: 同步和異步 同步與異步的例子 使用場景 代碼示例 專欄其他文章: </

(十二)深入淺出TCPIP之Nagle算法

未完待續 專欄其他文章: 理論篇: (一)深入淺出TCPIP之理解TCP報文格式和交互流程 (二)深入淺出TCPIP之再識TCP,理解TCP三次握手(上) (三)深入淺出TCPIP之再識TCP,理解TCP四次揮手(上) (四)深入淺出TCPIP之TCP三次握手和四次揮手(下)的抓包分析 (五)深入淺出T…

折紙的折痕(RVL中序遍歷)

這個題我見到過不止一次。筆試面試。 你拿個紙折一折會發現是這樣的&#xff1a; 這棵樹左子樹是紙的下半部分&#xff0c;右子樹是紙的上半部分。 下折痕指的是折痕突起的方向是紙的背面。 可以看出折痕是一棵滿二叉樹&#xff0c;根節點是下折痕&#xff0c;每一棵子樹的左…

(十四)深入淺出TCPIP之初識UDP理解報文格式和交互流程

目錄 關于UDP 代碼示例 UDP數據報格式 再次理解UDP數據包

動態規劃做多了以后,總結的相關知識

動態規劃 Dynamic Programming DP 準則 動態規劃的本質&#xff0c;是對問題狀態的定義和狀態轉移方程的定義。 動態規劃是通過拆分問題&#xff0c;定義問題狀態和狀態之間的關系&#xff0c;使得問題能夠以遞推&#xff08;或者說分治&#xff09;的方式去解決。 如何拆分…

(十三) 深入淺出TCPIP之TCP套接字參數

專欄其他文章&#xff1a; 理論篇&#xff1a; &#xff08;一&#xff09;深入淺出TCPIP之理解TCP報文格式和交互流程 (二&#xff09;深入淺出TCPIP之再識TCP&#xff0c;理解TCP三次握手&#xff08;上&#xff09; (三&#xff09;深入淺出TCPIP之再識TCP&#xff0c;理解…

(七)深入淺出TCPIP之深入淺出TCPIP之TCP重傳機制

目錄 TCP重傳機制 超時重傳機制 快速重傳機制 專欄其他文章: 理論篇: (一)深入淺出TCPIP之理解TCP報文格式和交互流程 (二)深入淺出TCPIP之再識TCP,理解TCP三次握手(上) (三)深入淺出TCPIP之再識TCP,理解TCP四次揮手(上&#x

那些年,我們信了課本里的那些鬼話

教材永遠都是有錯誤的&#xff0c;從小學到大學&#xff0c;我們不斷的學習了很多錯誤知識。 斑羚飛渡 在我們學習的很多小學課文里&#xff0c;有很多是錯誤文章&#xff0c;或者說是假課文。像《斑羚飛渡》&#xff1a; 隨著鐮刀頭羊的那聲吼叫&#xff0c;整個斑羚群迅速分…

(十一)深入淺出TCPIP之TCP粘包問題

目錄 粘包和拆包問題 保護消息邊界和流 粘包、拆包場景 為什么會發生TCP粘包、拆包呢?

Linux必懂知識大總結(上)

CPU top top&#xff1a;查看每個進程的情況 在top模式下&#xff0c;輸入1&#xff1a;查看每個CPU的性能數據&#xff0c;注意觀察是否有CPU100%占用率 CPU參數含義&#xff1a; 1&#xff09;us過高表示Java應用程序消耗了大量CPU&#xff0c;需要定位是哪一個線程&#x…

(十六)深入淺出TCPIP之Hello CDN

專欄其他文章 理論篇: (一)深入淺出TCPIP之理解TCP報文格式和交互流程 (二)深入淺出TCPIP之再識TCP,理解TCP三次握手(上) (三)深入淺出TCPIP之再識TCP,理解TCP四次揮手(上) (四)深入淺出TCPIP之TCP三次握手和四次揮手(下)的抓包分析 (五)深入淺出TCPIP之TCP流…