1. 程式人生 > >來討論一下這些常見的 Redis 面試題

來討論一下這些常見的 Redis 面試題

Redis應該算面試中必問的一個知識點,但是發現很多童鞋並不熟悉這塊,這篇就常見的一些問題做一些整理,有不對的地方歡迎留言指正!

1.Redis支援的資料型別?

String(字串)

格式: set key value

string型別是二進位制安全的。意思是redis的string可以包含任何資料。比如jpg圖片或者序列化的物件 。

string型別是Redis最基本的資料型別,一個鍵最大能儲存512MB。

Hash(雜湊)

格式: hmset name key1 value1 key2 value2

Redis hash 是一個鍵值(key=>value)對集合。

Redis hash是一個string型別的field和value的對映表,hash特別適合用於儲存物件。

List(列表)

Redis 列表是簡單的字串列表,按照插入順序排序。你可以新增一個元素到列表的頭部(左邊)或者尾部(右邊)

格式: lpush name value

在 key 對應 list 的頭部新增字串元素

格式: rpush name value

在 key 對應 list 的尾部新增字串元素

格式: lrem name index

key 對應 list 中刪除 count 個和 value 相同的元素

格式: llen name

返回 key 對應 list 的長度

Set(集合)

格式: sadd name value

Redis的Set是string型別的無序集合。

集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1)。

zset(sorted set:有序集合)

格式: zadd name score value

Redis zset 和 set 一樣也是string型別元素的集合,且不允許重複的成員。

不同的是每個元素都會關聯一個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。

zset的成員是唯一的,但分數(score)卻可以重複。

2.什麼是Redis持久化?Redis有哪幾種持久化方式?優缺點是什麼?

持久化就是把記憶體的資料寫到磁碟中去,防止服務宕機了記憶體資料丟失。

Redis 提供了兩種持久化方式:RDB(預設) 和AOF

RDB:

rdb是Redis DataBase縮寫

功能核心函式rdbSave(生成RDB檔案)和rdbLoad(從檔案載入記憶體)兩個函式

AOF:

Aof是Append-only file縮寫

每當執行伺服器(定時)任務或者函式時flushAppendOnlyFile 函式都會被呼叫, 這個函式執行以下兩個工作

aof寫入儲存:

  • WRITE:根據條件,將 aof_buf 中的快取寫入到 AOF 檔案
  • SAVE:根據條件,呼叫 fsync 或 fdatasync 函式,將 AOF 檔案儲存到磁碟中。

儲存結構:

內容是redis通訊協議(RESP )格式的命令文字儲存。

比較:

  • aof檔案比rdb更新頻率高,優先使用aof還原資料。
  • aof比rdb更安全也更大
  • rdb效能比aof好
  • 如果兩個都配了優先載入AOF

剛剛上面你有提到redis通訊協議(RESP ),能解釋下什麼是RESP?有什麼特點?(可以看到很多面試其實都是連環炮,面試官其實在等著你回答到這個點,如果你答上了對你的評價就又加了一分)

RESP 是redis客戶端和服務端之前使用的一種通訊協議;

RESP 的特點:實現簡單、快速解析、可讀性好

For Simple Strings the first byte of the reply is "+" 回覆
For Errors the first byte of the reply is "-" 錯誤
For Integers the first byte of the reply is ":" 整數
For Bulk Strings the first byte of the reply is "$" 字串
For Arrays the first byte of the reply is "*" 陣列

持久化在面試中問到的頻率較高,重點學一下,篇幅有限,具體點下面的文章:

10分鐘徹底理解Redis的持久化機制:RDB和AOF

3.Redis 有哪些架構模式?講講各自的特點

單機版

特點:

簡單

問題:

  • 記憶體容量有限
  • 處理能力有限
  • 無法高可用。

主從複製

Redis 的複製(replication)功能允許使用者根據一個 Redis 伺服器來建立任意多個該伺服器的複製品,其中被複制的伺服器為主伺服器(master),而通過複製創建出來的伺服器複製品則為從伺服器(slave)。

只要主從伺服器之間的網路連線正常,主從伺服器兩者會具有相同的資料,主伺服器就會一直將發生在自己身上的資料更新同步 給從伺服器,從而一直保證主從伺服器的資料相同。

特點:

  • master/slave 角色
  • master/slave 資料相同
  • 降低 master 讀壓力在轉交從庫

問題:

  • 無法保證高可用
  • 沒有解決 master 寫的壓力

哨兵

Redis sentinel 是一個分散式系統中監控 redis 主從伺服器,並在主伺服器下線時自動進行故障轉移。其中三個特性:

  • 監控(Monitoring):Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。
  • 提醒(Notification):當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程式傳送通知。
  • 自動故障遷移(Automatic failover):當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作。

特點:

  • 保證高可用
  • 監控各個節點
  • 自動故障遷移

缺點:

  • 主從模式,切換需要時間丟資料
  • 沒有解決 master 寫的壓力

叢集(proxy 型)

Twemproxy 是一個 Twitter 開源的一個 redis 和 memcache 快速/輕量級代理伺服器;Twemproxy 是一個快速的單執行緒代理程式,支援 Memcached ASCII 協議和 redis 協議。

特點:

  • 多種 hash 演算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
  • 支援失敗節點自動刪除
  • 後端 Sharding 分片邏輯對業務透明,業務方的讀寫方式和操作單個 Redis 一致

缺點:

  • 增加了新的 proxy,需要維護其高可用。
  • failover 邏輯需要自己實現,其本身不能支援故障的自動轉移可擴充套件性差,進行擴縮容都需要手動干預

叢集(直連型):

從redis 3.0之後版本支援redis-cluster叢集,Redis-Cluster採用無中心結構,每個節點儲存資料和整個叢集狀態,每個節點都和其他所有節點連線。

特點:

  • 無中心架構(不存在哪個節點影響效能瓶頸),少了 proxy 層。
  • 資料按照 slot 儲存分佈在多個節點,節點間資料共享,可動態調整資料分佈。
  • 可擴充套件性,可線性擴充套件到 1000 個節點,節點可動態新增或刪除。
  • 高可用性,部分節點不可用時,叢集仍可用。通過增加 Slave 做備份資料副本
    -實現故障自動 failover,節點之間通過 gossip 協議交換狀態資訊,用投票機制完成 Slave到 Master 的角色提升。

缺點:

  • 資源隔離性較差,容易出現相互影響的情況。
  • 資料通過非同步複製,不保證資料的強一致性

高可用Redis架構分析搭建,可以參考:高可用Redis服務架構分析與搭建

4.使用過Redis分散式鎖麼,它是怎麼實現的?

先拿setnx來爭搶鎖,搶到之後,再用expire給鎖加一個過期時間防止鎖忘記了釋放。

如果在setnx之後執行expire之前程序意外crash或者要重啟維護了,那會怎麼樣?

set指令有非常複雜的引數,這個應該是可以同時把setnx和expire合成一條指令來用的!

5.使用過Redis做非同步佇列麼,你是怎麼用的?有什麼缺點?

一般使用list結構作為佇列,rpush生產訊息,lpop消費訊息。當lpop沒有訊息的時候,要適當sleep一會再重試。

缺點:

  • 在消費者下線的情況下,生產的訊息會丟失,得使用專業的訊息佇列如rabbitmq等。

能不能生產一次消費多次呢?

使用pub/sub主題訂閱者模式,可以實現1:N的訊息佇列。

6.什麼是快取穿透?如何避免?什麼是快取雪崩?何如避免?

快取穿透

一般的快取系統,都是按照key去快取查詢,如果不存在對應的value,就應該去後端系統查詢(比如DB)。一些惡意的請求會故意查詢不存在的key,請求量很大,就會對後端系統造成很大的壓力。這就叫做快取穿透。

如何避免?

  • 對查詢結果為空的情況也進行快取,快取時間設定短一點,或者該key對應的資料insert了之後清理快取。
  • 對一定不存在的key進行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。

快取雪崩

當快取伺服器重啟或者大量快取集中在某一個時間段失效,這樣在失效的時候,會給後端系統帶來很大壓力。導致系統崩潰。

如何避免?

  • 在快取失效後,通過加鎖或者佇列來控制讀資料庫寫快取的執行緒數量。比如對某個key只允許一個執行緒查詢資料和寫快取,其他執行緒等待。
  • 做二級快取,A1為原始快取,A2為拷貝快取,A1失效時,可以訪問A2,A1快取失效時間設定為短期,A2設定為長期
  • 不同的key,設定不同的過期時間,讓快取失效的時間點儘量均勻。

這道相當常見,詳細再參考下文,一定熟練掌握:Redis快取雪崩、快取擊穿、快取穿透和常見的幾種快取模式

7.Redis常用命令

管理命令

# dbsize 返回當前資料庫 key 的數量。
# info 返回當前 redis 伺服器狀態和一些統計資訊。
# monitor 實時監聽並返回redis伺服器接收到的所有請求資訊。
# shutdown 把資料同步儲存到磁碟上,並關閉redis服務。
# config get parameter 獲取一個 redis 配置引數資訊。(個別引數可能無法獲取)
# config set parameter value 設定一個 redis 配置引數資訊。(個別引數可能無法獲取)
# config resetstat 重置 info 命令的統計資訊。(重置包括:keyspace 命中數、
# keyspace 錯誤數、 處理命令數,接收連線數、過期 key 數)
# debug object key 獲取一個 key 的除錯資訊。
# debug segfault 製造一次伺服器當機。
# flushdb 刪除當前資料庫中所有 key,此方法不會失敗。小心慎用
# flushall 刪除全部資料庫中所有 key,此方法不會失敗。小心慎用

 

工具命令

#redis-server:Redis 伺服器的 daemon 啟動程式
#redis-cli:Redis 命令列操作工具。當然,你也可以用 telnet 根據其純文字協議來操作
#redis-benchmark:Redis 效能測試工具,測試 Redis 在你的系統及你的配置下的讀寫效能
$redis-benchmark -n 100000 –c 50
#模擬同時由 50 個客戶端傳送 100000 個 SETs/GETs 查詢
#redis-check-aof:更新日誌檢查
#redis-check-dump:本地資料庫檢查

 

8.Redis單例、主從模式、sentinel以及叢集的配置方式及優缺點對比

Redis單例、主從模式、sentinel以及叢集的配置方式及優缺點對比

9.為什麼Redis 單執行緒卻能支撐高併發?

為什麼Redis 單執行緒卻能支撐高併發?

10.Redis常見效能問題和解決方案:

1).Master寫記憶體快照,save命令排程rdbSave函式,會阻塞主執行緒的工作,當快照比較大時對效能影響是非常大的,會間斷性暫停服務,所以Master最好不要寫記憶體快照。

2).Master AOF持久化,如果不重寫AOF檔案,這個持久化方式對效能的影響是最小的,但是AOF檔案會不斷增大,AOF檔案過大會影響Master重啟的恢復速度。Master最好不要做任何持久化工作,包括記憶體快照和AOF日誌檔案,特別是不要啟用記憶體快照做持久化,如果資料比較關鍵,某個Slave開啟AOF備份資料,策略為每秒同步一次。

3).Master呼叫BGREWRITEAOF重寫AOF檔案,AOF在重寫的時候會佔大量的CPU和記憶體資源,導致服務load過高,出現短暫服務暫停現象。

4).Redis主從複製的效能問題,為了主從複製的速度和連線的穩定性,Slave和Master最好在同一個區域網內

Redis效能分析相關問題,限於篇幅,給出文章連結:

Redis 效能問題分析(好文推薦)

11.Redis的併發競爭問題如何解決?

Redis為單程序單執行緒模式,採用佇列模式將併發訪問變為序列訪問。Redis本身沒有鎖的概念,Redis對於多個客戶端連線並不存在競爭,但是在Jedis客戶端對Redis進行併發訪問時會發生連線超時、資料轉換錯誤、阻塞、客戶端關閉連線等問題,這些問題均是由於客戶端連線混亂造成。對此有2種解決方法:

  • 客戶端角度,為保證每個客戶端間正常有序與Redis進行通訊,對連線進行池化,同時對客戶端讀寫Redis操作採用內部鎖synchronized。
  • 伺服器角度,利用setnx實現鎖。

注:對於第一種,需要應用程式自己處理資源的同步,可以使用的方法比較通俗,可以使用synchronized也可以使用lock;第二種需要用到Redis的setnx命令,但是需要注意一些問題。

12.說說Redis的記憶體淘汰策略

直接點這裡:Redis的記憶體淘汰策略

13.Redis最適合的場景

Redis最適合所有資料in-momory的場景,雖然Redis也提供持久化功能,但實際更多的是一個disk-backed的功能,跟傳統意義上的持久化有比較大的差別,那麼可能大家就會有疑問,似乎Redis更像一個加強版的Memcached,那麼何時使用Memcached,何時使用Redis呢?

如果簡單地比較Redis與Memcached的區別,大多數都會得到以下觀點:

  • Redis不僅僅支援簡單的k/v型別的資料,同時還提供list,set,zset,hash等資料結構的儲存。
  • Redis支援資料的備份,即master-slave模式的資料備份。
  • Redis支援資料的持久化,可以將記憶體中的資料保持在磁碟中,重啟的時候可以再次載入進行使用。

會話快取(Session Cache)

最常用的一種使用Redis的情景是會話快取(session cache)。用Redis快取會話比其他儲存(如Memcached)的優勢在於:Redis提供持久化。當維護一個不是嚴格要求一致性的快取時,如果使用者的購物車資訊全部丟失,大部分人都會不高興的,現在,他們還會這樣嗎?

幸運的是,隨著 Redis 這些年的改進,很容易找到怎麼恰當的使用Redis來快取會話的文件。甚至廣為人知的商業平臺Magento也提供Redis的外掛。

全頁快取(FPC)

除基本的會話token之外,Redis還提供很簡便的FPC平臺。回到一致性問題,即使重啟了Redis例項,因為有磁碟的持久化,使用者也不會看到頁面載入速度的下降,這是一個極大改進,類似PHP本地FPC。

再次以Magento為例,Magento提供一個外掛來使用Redis作為全頁快取後端。

此外,對WordPress的使用者來說,Pantheon有一個非常好的外掛 wp-redis,這個外掛能幫助你以最快速度載入你曾瀏覽過的頁面。

佇列

Reids在記憶體儲存引擎領域的一大優點是提供 list 和 set 操作,這使得Redis能作為一個很好的訊息佇列平臺來使用。Redis作為佇列使用的操作,就類似於本地程式語言(如Python)對 list 的 push/pop 操作。

如果你快速的在Google中搜索“Redis queues”,你馬上就能找到大量的開源專案,這些專案的目的就是利用Redis建立非常好的後端工具,以滿足各種佇列需求。例如,Celery有一個後臺就是使用Redis作為broker,你可以從這裡去檢視。

排行榜/計數器

Redis在記憶體中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(Sorted Set)也使得我們在執行這些操作的時候變的非常簡單,Redis只是正好提供了這兩種資料結構。所以,我們要從排序集合中獲取到排名最靠前的10個使用者–我們稱之為“user_scores”。

當然,這是假定你是根據你使用者的分數做遞增的排序。如果你想返回使用者及使用者的分數,你需要這樣執行:ZRANGE user_scores 0 10 WITHSCORES

Agora Games就是一個很好的例子,用Ruby實現的,它的排行榜就是使用Redis來儲存資料的,你可以在這裡看到。

釋出/訂閱

最後(但肯定不是最不重要的)是Redis的釋出/訂閱功能。釋出/訂閱的使用場景確實非常多。我已看見人們在社交網路連線中使用,還可作為基於釋出/訂閱的指令碼觸發器,甚至用Redis的釋出/訂閱功能來建立聊天系