看了這篇Redis,開發面試再也不怕

1) 什麼是 Redis

  • Redis(Remote Dictionary Server)是一個使用「C語言」 編寫的,開源的(BSD許可)高性能「非關係型(NoSQL)」 的鍵值對數據庫
  • Redis能夠存儲鍵和五種不一樣類型的值之間的映射。
  • Redis 支持不少特性,例如將內存中的數據持久化到硬盤中,使用複製來擴展讀性能,使用分片來擴展寫性能。因此讀寫速度很是快,所以Redis被普遍應用於緩存方向,每秒能夠處理超過「10萬次」讀寫操做,是已知性能最快的key-value 數據庫。另外 Redis 也常常用來作「分佈式鎖」,並且 Redis 支持「事務」「持久化」「LUA腳本」「LRU驅動事件」「多種集羣方案」等。

2)Redis爲何那麼快

  • 徹底基於內存,絕大部分請求是純粹的內存操做
  • 數據結構簡單,對數據操做也簡單,Redis中的數據結構是專門爲各類場景設計的
  • 採用單線程,避免了沒必要要的上下文切換和競爭條件,也不存在多進程或者多線程致使的切換而消耗CPU,不用去考慮各類鎖的問題,不存在加鎖釋放鎖操做,沒有由於可能出現死鎖而致使的性能消耗
  • 使用多路 I/O 複用模型,非阻塞 IO

3)Redis 的優缺點

「優勢:」redis

  • 讀寫速度快,由於數據存在內存中,相似於HashMap
  • 支持豐富的數據類型,支持 String,List,Hash,Set,SortSet(Zset)
  • 支持事務(一致性和隔離性),利用好RDB和AOF也能實現持久性
  • 支持主從複製,主機會自動將數據同步到從機,能夠進行讀寫分離

「缺點:」數據庫

  • 緩存和數據庫雙寫一致性問題
  • 緩存雪崩、穿透、擊穿問題
  • Redis 較難支持在線擴容,在集羣容量達到上限時擴容會變得很複雜
  • 主機宕機前會有部分數據未能及時同步到從機,切換IP後還會引入數據不一致問題,下降了系統的可用性
  • 緩存的併發競爭問題

4)Redis與Memcache的區別

  • 「數據類型」
    • Memcached僅支持字符串類型
    • Redis 支持五種不一樣的數據類型,能夠更靈活地解決問題
  • 「數據持久化」
    • Memcached 不支持持久化
    • Redis 支持兩種持久化策略:RDB快照 和 AOF 日誌
  • 「分佈式」
    • Memcached 不支持分佈式,只能經過在客戶端使用一致性哈希來實現分佈式存儲,這種方式在存儲和查詢時都須要如今客戶端計算一次數據所在的節點
    • Redis Cluster 支持分佈式
  • 「內存管理機制」
    • Redis 中,並非全部數據都一直存儲在內存中,能夠將一些好久沒用的 value 交換到磁盤
    • Memcached 的數據一直會在內存中,它將內存分割成特定長度的塊來存儲數據,以徹底解決內存碎片的問題,可是這種方式會使得內存的利用率不高,例如塊的大小爲 128 bytes,只存儲 100 bytes 的數據,那麼剩下的 28 bytes 就浪費了

5)Redis有哪些數據類型

  • 「String(字符串)」
    • 簡介: 二進制安全
    • 能夠存儲的值: 字符串,整數或者浮點數,還有jpg圖片或者序列化對象
    • 操做: 對整個字符串或者字符串的其中一部分執行操做,對整數和浮點數執行自增或者自減操做
    • 應用場景: 作簡單的鍵值對緩存
    • 實際使用:
  > set test cbuc
ok
 ----------------------------
  > get test
"cbuc"
 ----------------------------
  > del test
(integer) 1
 ----------------------------
  >get test
(nil)
  • 「List(列表)」
    • 簡介: 鏈表(雙向鏈表)
    • 能夠存儲的值: 列表
    • 操做: 從兩端壓入或者彈出元素,對單個或者多個元素進行修改,只保留一個範圍內的元素
    • 應用場景: 最新消息排行;消息隊列
    • 實際使用:
  > rpush test c1
(integer) 1
  > rpush test c2
(integer) 2
  > rpush test c2
(integer) 3
  > lrange test 0 -1
1) "c1"
2) "c2"
3) "c3"
  > lindex test 1
"c2"
  > lpop test
"c1"
  > lrange test 0 -1
"c2"
"c2" 
  • 「Hash(字典)」
    • 簡介: 鍵值對集合,即編程語言中的map類型
    • 能夠存儲的值: 適合存儲對象,而且能夠像數據庫中的update同樣,只修改某一項的屬性值
    • 操做: 添加、獲取、移除單個鍵值對,獲取全部鍵值對,檢查某個鍵是否存在
    • 應用場景: 存儲、讀取、修改用戶屬性
    • 實際使用:
  > hset test name cbuc
(integer) 1
  > hset test age 23
(integer) 1
  > hgetall test
1)"name"
2)"cbuc"
3)"age"
4)"23"
  > hdel test age
(integer) 1
  > hget test name 
"cbuc"
  > hgetall test
1)"name"
2)"cbuc"   
  • 「Set(集合)」
    • 簡介: hash表實現,元素不重複
    • 能夠存儲的值: 無序集合
    • 操做: 添加、獲取、移除單個元素,檢查一個元素是否已經存在於集合中,計算交集、並集、差集從集合裏面隨機獲取元素
    • 應用場景: 共同好友;利用惟一性,統計訪問網站的全部IP
    • 實際使用:
  > sadd test c1
(integer) 1
  > sadd test c2
(integer) 1
  > sadd test c1
(integer) 0
  >smembers test
1)"c1"
2)"c2"
  > sismember test c3
(integer) 0
  > sismember test c1
(integer) 1
  > srem test c1
(integer) 1
  > smembers test
1)"c2"   
  • 「ZSet(有序集合)」
    • 簡介: 將 set 中的元素增長一個權重參數score,元素按score有序排列
    • 能夠存儲的值: 有序集合
    • 操做: 添加、獲取、刪除元素,根據分值範圍或者成員來獲取元素,計算一個鍵的排名
    • 應用場景: 排行榜;帶權重的消息隊列
    • 實際使用:
  > zadd test 92 math
(integer) 1
  > zadd test 88 english
(integer) 1
  > zadd test 92 score
(integer) 1
  > zrange test 0 -1 withscores
1)"english"
2)"88"
3)"math"
4)"92"
  > zrangebyscore test 80 90 withscores
1)"english"
2)"88"
  > zrem test english
(integer) 1
  > zrange test 0 -1 withscores
3)"math"
4)"92" 

「高級用法:」編程

  • BitMap位圖是支持按 bit 位來存儲信息,能夠用來實現 布隆過濾器(BloomFilter)
  • HyperLogLog供不精確的去重計數功能,比較適合用來作大規模數據的去重統計,例如統計 UV
  • Geospatial能夠用來保存地理位置,並做位置距離計算或者根據半徑計算位置等。能夠用來實現附近的人,或者計算最優地圖路徑

6)什麼是Redis持久化

持久化就是把內存中的數據寫到磁盤中去,防止服務宕機了內存數據丟失。緩存

7)Redis的持久化機制

Redis提供兩種持久化機制:RDB快照(默認)和AOF(機制)安全

「RDB」服務器

RDB(Redis DataBase)是Redis中默認的持久化方式。按照必定的時間將內存的數據以快照的形式保存到磁盤中,會產生dump.rdb數據文件,能夠經過配置文件中的save參數來定義快照的週期。網絡

圖片「原理:」 forkcow。fork 是指 redis間隔一段時間會「fork」 一個子進程,子線程將數據寫到磁盤上一個臨時RDB文件中,當子進程寫完臨時文件後,將原來的RDB替換掉,這樣的好處是能夠「cow(copy-on-wirte)」「優勢:」數據結構

  • 方便持久化,只有一個文件 dump.rdb
  • 容災性好,一個文件能夠保存到安全的磁盤中
  • 性能最大化,fork 子進程來完成寫操做,讓主進程繼續處理命令,因此是IO最大化。使用單獨子進程來進行持久化,主進程不會進行任何IO操做,保證 redis 的高性能

「缺點:」多線程

  • 數據安全性低,RDB是間隔一段時間來進行持久化,若是持久化之間 redis 發生故障,會發生數據丟失,因此這種方式更適合數據要求不嚴謹的時候用
  • 保存時間長,若是數據量很大,保存快照的時間會很長

「AOF」架構

AOF(Append-Only-File),是將 Redis 執行的每次寫命令記錄到單獨的日誌文件中,當重啓 Redis 會從新將持久化的日誌中文件恢復數據

圖片「原理:」 將寫命令添加到 AOF 文件(Append Only File)的末尾。使用AOF持久化須要設置同步選項,從而確保寫命令同步到磁盤文件上的時機。這是由於對文件進行寫入並不會立刻將內容同步到磁盤上,而是先存儲到緩衝區,而後由操做系統決定何時同步到磁盤。「同步選項:」

選項 同步頻率
no 讓操做系統決定什麼時候同步
always 每一個寫命令都同步
everysec 每秒同步一次
  • no: 並不能給服務器性能帶來多大的提高,並且也會增長系統奔潰時數據丟失的數量
  • always: 嚴重減低服務器性能
  • everysec: 這個選項比較合適,能夠保證系統奔潰時只會丟失一秒左右的數據,而且 Redis 每秒執行一次同步對服務器性能幾乎沒有任何影響

隨着服務器寫請求的增多,AOF文件會愈來愈大。Redis提供了一種將AOF重寫的特性「auto-aof-rewrite」,可以去除AOF文件中的冗餘寫命令

「優勢:」

  • 數據安全,AOF 持久化能夠配置 appendfsync 屬性中的always,沒進行一次寫命令操做就記錄到 AOF 文件中一次、
  • 一致性,經過 append 模式寫文件,即便中途服務器宕機,能夠經過 redis-check-aof 工具解決數據一致性問題

「缺點:」

  • AOF 文件比RDB文件大,並且恢復速度慢
  • 數據集大的時候比 RDB 啓動效率低

二者比較

  • AOF 文件比 RDB 更新頻率高,優先使用 AOF 還原數據
  • AOF 比 RDB 更安全也更強大
  • RDB 性能比 AOF 好
  • 若是兩個都配了優先加載 AOF

8)如何選擇合適的持久化方式

通常來講二者都會配置。若是單獨用 「RDB」 的話你會丟失不少數據,單獨用 「AOF」,你數據恢復沒有 「RDB」 來的快,若是系統出現問題的時候咱們能夠先用 「RDB」 恢復,而後用 「AOF」 補全數據。冷熱備份一塊兒用,才能保證高健壯性的系統。

9)Redis的過時鍵刪除策略

  • 定時刪除每一個設置過時時間的 key 都須要建立一個定時器,到過時時間就會當即清除。該策略能夠當即清除過時的數據,對內存很友好;可是會佔用大量的CPU資源來處理過時的數據,從而影響緩存的響應時間和吞吐量
  • 惰性刪除只有當訪問一個 key 時,纔會判斷該 key 是否已過時,是則刪除。該策略能夠最大化節省CPU資源,卻對內存很是不友好。極端狀況下可能出現大量的過時 key 沒有再次被訪問,從而不會被清除,佔用大量內存
  • 按期刪除每隔必定時間,會掃描必定數量的 expires 字典中的 key,並清除其中已過時的 key。該策略是前二者的一個折中方法。經過調整定時掃描的時間間隔和每次掃描的限定耗時,能夠在不一樣狀況下使得CPU和內存資源達到最優的平衡效果。

Redis 通常同時使用 惰性過時 和 按期過時 兩種過時策略

10)Redis 內存淘汰策略

「設置過時時間的鍵空間選擇性移除」

  • volatile-lru:嘗試回收最少使用的鍵使得新添加的數據有空間存放。
  • volatile-random:回收隨機的鍵使得新添加的數據有空間存放
  • volatile-ttl:優先回收存活時間較短的鍵使得新添加的數據有空間存放

「全局的鍵空間選擇性移除」

  • allkeys-lru:嘗試回收最少使用的鍵使得新添加的數據有空間存放。
  • allkeys-random:回收隨機的鍵使得新添加的數據有空間存放
  • noeviction:當內存達到限制而且客戶端嘗試執行,會返回錯誤

11)Redis 事務

Redis 事務的本質是經過 「MULTI」「EXEC」「WATCH」「DISCARD」四個原語實現的。事務支持一次執行多個命令,一個事務中全部命令都會被序列化。在事務執行過程,會按照順序串行化執行隊列中的命令,其餘客戶端提交的命令請求不會插入到事務執行命令序列中。總結:Redis 事務就是一次性、順序性、排他性的執行一個隊列中的一系列命令。

  • 「Redis 不支持回滾」,Redis 在事務失敗時不進行回滾,而是繼續執行餘下的命令,因此 Redis 的內部能夠保持簡單且快速。
  • 若是在一個事務中的命令出現錯誤,那麼全部的命令都不會執行
  • 若是在一個事務中出現運行錯誤,那麼正確的命令會被執行四個原語
  • 「WATCH:」 是一個樂觀鎖,能夠爲 Redis 事務提供 check-and-set(CAS)行爲,能夠監控一個或多個鍵。一旦其中有一個鍵被修改(或刪除),以後的事務就不會執行,監控一直持續到EXEC命令
  • 「MULTI:」 用於開啓一個事務,它老是返回OK。MULTI執行以後,客戶端能夠繼續向服務器發送任意多條命令,這些命令不會當即被執行,而是被放到一個隊列中,當 EXEC命令被調用時,全部隊列中的命令纔會被執行
  • 「EXEC:」 執行全部事務塊內的命令,返回事務塊內全部命令的返回值,按命令執行的前後順序排列,當操做被打斷時,返回控制 nil
  • 「DISCARD:」 調用 DISCARD ,客戶端能夠清空事務隊列,並放棄執行事務,而且客戶端會從事務狀態中退出

12)Redis設置過時時間和永久有效

EXPIRE 和 PERSIST 命令

13)緩存雪崩

緩存雪崩是指緩存同一時間大面積的失效,因此,後面的請求都會落到數據庫上,形成數據庫短期內承受大量請求而崩掉解決方法:

  • 緩存數據的過時時間設置隨機,防止同一時間大量數據過時現象發生
  • 通常併發量不是特別多的時候,使用最多的解決方案是加鎖排隊
  • 搭建集羣,若是一臺 Redis 掛掉以後,還有其餘的能夠繼續工做

14)緩存擊穿

緩存擊穿是指緩存中沒有但數據庫中有的數據(通常是緩存時間到期),這時因爲併發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引發數據庫壓力瞬間增大解決方法:

  • 設置熱點數據永遠不過時
  • 可使用互斥鎖更新,保證同一個進程中針對同一個數據不會併發請求到 DB,減少 DB 壓力
  • 使用隨機退避方式,失效時隨機 sleep 一個很短的時間,再次查詢,若是失敗再執行更新

15)緩存穿透

緩存穿透是指緩存和數據庫中都沒有的數據,致使全部的請求都落到數據庫上,形成數據庫短期內承受大量請求而崩掉解決方法:

  • 對於不存在的數據,在緩存中保存一個數據進行標記,並設置上過時時間,防止相同的數據請求再次訪問 DB。
  • 使用 「BloomFilter」 過濾器,布隆過濾器的特色是存在性檢測,若是布隆過濾器中不存在,那麼數據必定不存在;若是布隆過濾器中存在,實際數據也有可能會不存在。

16)Redis主從架構

單機的 Redis,可以承載的 QPS 大概在上萬到幾萬不等,對於緩存來講,通常都是用來支撐「讀高併發」的。若是一臺機器讀寫合一的那會很容易發生問題。所以會採用主從架構,讓 master 去處理寫操做,而後把數據同步到 slave 上,slave 負責讀操做。這樣就會分發掉大量的請求,並且在擴容的時候還能夠輕鬆實現水平擴容。圖片當啓動一臺 slave 的時候,它會發送一個 psync 命令到 master ,若是是第一次同步,主節點會作一次「bgsave」,並同時將後續修改操做記錄到內存「buffer」,待完成後將「RDB」文件全量同步到複製節點,複製節點接收完成後將「RDB」鏡像加載到內存而後寫入本地磁盤。處理完成後,再通知主節點將期間修改的操做記錄同步到複製節點進行重放就完成了同步過程。後續的增量數據經過「AOF」日誌同步便可,相似於數據庫的「binlog」

17)Redis實現分佈式鎖

簡單來講就是先拿setnx來爭搶鎖,搶到以後,再用expire給鎖加一個過時時間防止鎖忘記了釋放SETNX 是【SET if Not eXists】(若是不存在,則 SET)的簡寫。當且僅當 key 不存在,將 key 的值設爲 value。若給定的 key 已經存在,則 SETNX 不作任何動做。返回值:設置成功,返回 1 。設置失敗,返回 0 。

18)Redis的同步機制

Redis可使用主從同步,從從同步。第一次同步時,主節點會作一次「bgsave」,並同時將後續修改操做記錄到內存「buffer」,待完成後將「RDB」文件全量同步到複製節點,複製節點接收完成後將「RDB」鏡像加載到內存而後寫入本地磁盤。處理完成後,再通知主節點將期間修改的操做記錄同步到複製節點進行重放就完成了同步過程。後續的增量數據經過「AOF」日誌同步便可,相似於數據庫的「binlog」

19)Redis集羣原理

Redis Sentinel(哨兵)着眼於高可用,在master 宕機時會自動將slave提高爲master,繼續提供服務。Redis Cluster(集羣)着眼於擴展性,在單個redis內存不足時,使用Cluster進行分片存儲。「選主策略:」

  • slave 的 priority 設置的越低,優先級越高
  • 同等狀況下,slave 複製的數據越多優先級越高
  • 相同的條件下,runid 越小越容易被選中

20)Redis 哨兵模式

圖片哨兵必須用三個實例去保證本身的健壯性,哨兵 + 主從 「並不能保證數據不丟失」 ,可是能夠保證集羣的高可用。工做原理:

  • 每一個 Sentinel 節點以每秒一次的頻率向它所知的主服務器、從服務器和其餘 Sentinel 節點發送一個 PING 命令
  • 若是 一個實例舉例最後一次有效回覆 PING 命令的時間超過「down-after-milliseconds」 所指定的值,那麼這個實例就會被標記爲主觀下線
  • 若是一個主服務器被標記爲主觀下線,那麼正在監視這個服務器的全部 Sentinel 節點會以每秒一次的頻率確認主服務器的確進入了主觀下線狀態
  • 若是有足夠數量的 Sentinel (至少要達到配置文件中指定的數量)在指定的時間範圍內贊成這一判斷,那麼這個主服務器就會被標記爲客觀下線
  • 當主服務器被標記爲客觀下線後,Sentinel 節點會向下線主服務器的全部從服務器發送 INFO 命令的頻率從 10 秒一次改成每秒一次
  • Sentinel 節點會協商客觀下線主服務器的狀態,若是處於 SDOWN 狀態,則投票自動選出新的主節點,將剩下從節點指向新的主節點進行數據複製。
  • 當沒有足夠數量的 Sentinel 節點泳衣主服務器下線是,主服務器的客觀下線狀態就會被移除。或者當主服務器從新向 Sentinel 的 PING 命令返回有效回覆是,主服務器的主觀下線狀態就會被移除

21)腦裂問題及解決

何爲腦裂:

Redis 的集羣腦裂是指由於網絡問題,致使 redis master 節點跟 redis slave 節點和 sentinel 集羣處於不用的網絡分區,此時由於 sentinel 集羣沒法感知到 master 的存在,因此將 slave 節點提高爲 master 節點。此時存在兩個不一樣的 master 節點,就像是一個大腦分裂成了兩個。這時若是客戶端還在基於原來的 master 節點繼續寫入數據,那麼新的 master 節點將沒法同步這些數據,當網絡問題解決以後,sentinel集羣就會將原先的 master 節點降爲 slave 節點,此時再重新的 master 中同步數據,將會形成大量的數據丟失

圖片

解決:

  • 一般採用隔離(Fencing)機制
    • 共享存儲Fencing:確保只有一個 Master 往共享存儲中寫數據
    • 客戶端Fencing:確保只有一個 Master 能夠響應客戶端的請求
    • Slave Fencing:確保只有一個 Master 能夠向 Slave 下發命令
  • 配置文件中修改參數
min-replicas-to-write 3  # 表示鏈接到 master 的最少slave數量
min-replicas-max-lag 10  # 表示 slave 鏈接到 master 的最大延遲時間

按照上面配置,要求至少3個slave節點,且數據複製和同步的延遲不能超過10秒,不然的話 master 就會拒絕寫請求,配置了這兩個參數以後,若是發生集羣腦裂,原先的 master 節點接收到客戶端的寫入請求會拒絕,就能夠減小數據同步以後的數據丟失

相關文章
相關標籤/搜索