面試必問之redis

這裏是我作爲10年面試經驗總結的面試中必問問題

問題一 簡單介紹下redis

edis是當前比較熱門的NOSQL系統之一,它是一個開源的使用ANSI c語言編寫的key-value存儲系統(區別於MySQL的二維表格的形式存儲。)。和Memcache類似,但很大程度補償了Memcache的不足。和Memcache一樣,Redis數據都是緩存在計算機內存中,不同的是,Memcache只能將數據緩存到內存中,無法自動定期寫入硬盤,這就表示,一斷電或重啓,內存清空,數據丟失。所以Memcache的應用場景適用於緩存無需持久化的數據。而Redis不同的是它會週期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,實現數據的持久化

問題二 redis有哪幾種數據結構

1、string是redis最基本的類型,可以理解成與memcached一模一樣的類型,一個key對應一個value。value不僅是string,也可以是數字。string類型是二進制安全的,意思是redis的string類型可以包含任何數據,比如jpg圖片或者序列化的對象。string類型的值最大能存儲512M。
2、Hash是一個鍵值(key-value)的集合。redis的hash是一個string的key和value的映射表,Hash特別適合存儲對象。常用命令:hget,hset,hgetall等。
3、list列表是簡單的字符串列表,按照插入順序排序。可以添加一個元素到列表的頭部(左邊)或者尾部(右邊) 常用命令:lpush、rpush、lpop、rpop、lrange(獲取列表片段)等。應用場景:list應用場景非常多,也是Redis最重要的數據結構之一,比如twitter的關注列表,粉絲列表都可以用list結構來實現。數據結構:list就是鏈表,可以用來當消息隊列用。redis提供了List的push和pop操作,還提供了操作某一段的api,可以直接查詢或者刪除某一段的元素。實現方式:redis list的是實現是一個雙向鏈表,既可以支持反向查找和遍歷,更方便操作,不過帶來了額外的內存開銷。
4、set是string類型的無序集合。集合是通過hashtable實現的。set中的元素是沒有順序的,而且是沒有重複的。常用命令:sdd、spop、smembers、sunion等。應用場景:redis set對外提供的功能和list一樣是一個列表,特殊之處在於set是自動去重的,而且set提供了判斷某個成員是否在一個set集合中。
5、zset和set一樣是string類型元素的集合,且不允許重複的元素。常用命令:zadd、zrange、zrem、zcard等。使用場景:sorted set可以通過用戶額外提供一個優先級(score)的參數來爲成員排序,並且是插入有序的,即自動排序。當你需要一個有序的並且不重複的集合列表,那麼可以選擇sorted set結構。和set相比,sorted set關聯了一個double類型權重的參數score,使得集合中的元素能夠按照score進行有序排列,redis正是通過分數來爲集合中的成員進行從小到大的排序。實現方式:Redis sorted set的內部使用HashMap和跳躍表(skipList)來保證數據的存儲和有序,HashMap裏放的是成員到score的映射,而跳躍表裏存放的是所有的成員,排序依據是HashMap裏存的score,使用跳躍表的結構可以獲得比較高的查找效率,並且在實現上比較簡單。

問題三 redis雪崩、穿透、擊穿分別是因爲什麼原因導致,你一般是怎麼解決的

緩存穿透

原因

緩存穿透:key對應的數據在數據源並不存在,每次針對此key的請求從緩存獲取不到,請求都會到數據源,從而可能壓垮數據源。比如用一個不存在的用戶id獲取用戶信息,不論緩存還是數據庫都沒有,若黑客利用此漏洞進行攻擊可能壓垮數據庫。

解決方案

最常見的則是採用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被 這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。另外也有一個更爲簡單粗暴的方法(我們採用的就是這種),如果一個查詢返回的數據爲空(不管是數據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。

緩存擊穿

原因

緩存擊穿:key對應的數據存在,但在redis中過期,此時若有大量併發請求過來,這些請求發現緩存過期一般都會從後端DB加載數據並回設到緩存,這個時候大併發的請求可能會瞬間把後端DB壓垮。

解決方案

key可能會在某些時間點被超高併發地訪問,是一種非常“熱點”的數據。這個時候,需要考慮一個問題:緩存被“擊穿”的問題
方案一:使用互斥鎖(mutex key)
業界比較常用的做法,是使用mutex。簡單地來說,就是在緩存失效的時候(判斷拿出來的值爲空),不是立即去load db,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一個mutex key,當操作返回成功時,再進行load db的操作並回設緩存;否則,就重試整個get緩存的方法。
方案二:不設置失效時間,使用另外的定時任務去更新緩存數據

緩存雪崩

原因

緩存雪崩:當緩存服務器重啓或者大量緩存集中在某一個時間段失效,這樣在失效的時候,也會給後端系統(比如DB)帶來很大壓力。

解決方案

與緩存擊穿的區別在於這裏針對很多key緩存,前者則是某一個key。
緩存失效時的雪崩效應對底層系統的衝擊非常可怕!大多數系統設計者考慮用加鎖或者隊列的方式保證來保證不會有大量的線程對數據庫一次性進行讀寫,從而避免失效時大量的併發請求落到底層存儲系統上。還有一個簡單方案就時講緩存失效時間分散開,比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。

問題四 爲什麼redis要用單線程

redis 核心數據全都在內存裏,單線程的去操作 效率是最高的,爲什麼呢?
因爲多線程的本質就是 CPU 模擬出來多個線程的情況,這種模擬出來的情況就有一個代價,就是上下文的切換,對於一個內存的系統來說,它沒有上下文的切換就是效率最高的。redis 用 單個CPU 綁定一塊內存的數據,然後針對這塊內存的數據進行多次讀寫的時候,都是在一個CPU上完成的,所以它是單線程處理這個事。在內存的情況下,這個方案就是最佳方案。我們可以舉個例子來比較下單線程與多線程的成本:
一次CPU上下文的切換大概在 1500ns 左右。
從內存中讀取 1MB 的連續數據,耗時大約爲 250us,假設1MB的數據由多個線程讀取了1000次,那麼就有1000次時間上下文的切換,
那麼就有1500ns * 1000 = 1500us ,單線程的讀完1MB數據才250us ,如果多線程光是上下文的切換就用了1500us了,這還不算每次讀一點數據 的時間,

問題五 redis有哪幾種持久化方式

Redis 提供了 RDB 和 AOF 兩種持久化方式,RDB 是把內存中的數據集以快照形式寫入磁盤,實際操作是通過 fork 子進程執行,採用二進制壓縮存儲;AOF 是以文本日誌的形式記錄 Redis 處理的每一個寫入或刪除操作。

RDB 把整個 Redis 的數據保存在單一文件中,比較適合用來做災備,但缺點是快照保存完成之前如果宕機,這段時間的數據將會丟失,另外保存快照時可能導致服務短時間不可用。

AOF 對日誌文件的寫入操作使用的追加模式,有靈活的同步策略,支持每秒同步、每次修改同步和不同步,缺點就是相同規模的數據集,AOF 要大於 RDB,AOF 在運行效率上往往會慢於 RDB。

問題六 redis有哪幾種高可用部署方式

主從模式

在這裏插入圖片描述
使用一個Redis實例作爲主機,其餘的作爲備份機。主機和備份機的數據完全一致,主機支持數據的寫入和讀取等各項操作,而從機則只支持與主機數據的同步和讀取。也就是說,客戶端可以將數據寫入到主機,由主機自動將數據的寫入操作同步到從機。主從模式很好的解決了數據備份問題,並且由於主從服務數據幾乎是一致的,因而可以將寫入數據的命令發送給主機執行,而讀取數據的命令發送給不同的從機執行,從而達到讀寫分離的目的。

這種部署方案會存在主節點宕機後沒辦法進行寫操作問題,需要人工干預或者keeplive+VIP來進行主節點漂移

哨兵

redis-Sentinel(哨兵模式)是Redis官方推薦的高可用性(HA)解決方案,當用Redis做Master-slave的高可用方案時,假如master宕機了,Redis本身(包括它的很多客戶端)都沒有實現自動進行主備切換,而Redis-sentinel本身也是一個獨立運行的進程,它能監控多個master-slave集羣,發現master宕機後能進行切換

sentinel哨兵如下功能實現
(1)monitoring:監控redis是否正常運行
(2)notification:通知application錯誤信息
(3)failover:當某個master死掉,選擇另外一個slave升級爲master,更 新master-slave關係。
(4)configurationprovider:client通過sentinel獲取redis地址,並在failover時更新地址

2.sentinels and slaves autodiscovery(redis2.8及以上)
配置文件中只配置master地址,slave地址和sentinel地址可以自動發現。
(1)sentinels——sentinel之間通過redis pub/sub交換信息獲得。
(2)slaves——詢問master獲得。

3.sdown、odown、failover
故障檢測一般都是通過ping-pong機制,sentinel引入sdown(主觀下線)和odown(客觀下線)機制,目的應該是在集羣規模較大時,檢測更客觀
(1)sdwon——is-master-down-after-milliseconds(可配置)時間內ping-pong失敗。sdown的slave不能升級爲master。
(2)odown——超過一定數目(可配置)的sentinel認爲sdown,odown只針對master。
(3)failover——多數sentinel認爲odown。

redis集羣模式

redis集羣模式,同樣可以實現redis高可用部署,Redis Sentinel集羣模式中,隨着業務量和數據量增,到性能達到redis單節點瓶頸,垂直擴容受機器限制,水平擴容涉及對應用的影響以及數據遷移中數據丟失風險。針對這些痛點
Redis3.0推出cluster分佈式集羣方案,當遇到單節點內存,併發,流量瓶頸是,採用cluster方案實現負載均衡,cluster方案主要解決分片問題,即把整個數據按照規則分成多個子集存儲在多個不同幾點上,每個節點負責自己整個數據的一部分。
Redis Cluster採用哈希分區規則中的虛擬槽分區。虛擬槽分區巧妙地使用了哈希空間,使用分散度良好的哈希函數把所有的數據映射到一個固定範圍內的整數集合,整數定義爲槽(slot)。Redis Cluster槽的範圍是0 ~ 16383。槽是集羣內數據管理和遷移的基本單位。採用大範圍的槽的主要目的是爲了方便數據的拆分和集羣的擴展,每個節點負責一定數量的槽。Redis Cluster採用虛擬槽分區,所有的鍵根據哈希函數映射到0 ~ 16383,計算公式:slot = CRC16(key)&16383。每一個實節點負責維護一部分槽以及槽所映射的鍵值數據。下圖展現一個五個節點構成的集羣,每個節點平均大約負責3276個槽,以及通過計算公式映射到對應節點的對應槽的過程。

redis有哪幾種淘汰策略

redis有六種淘汰策略

  • noeviction: 不刪除策略, 達到最大內存限制時, 如果需要更多內存, 直接返回錯誤信息。 大多數寫命令都會導致佔用更多的內存(有極少數會例外, 如 DEL )。
  • allkeys-lru: 所有key通用; 優先刪除最近最少使用(less recently used ,LRU) 的 key。
  • volatile-lru: 只限於設置了 expire 的部分; 優先刪除最近最少使用(less - recently used ,LRU) 的 key。
  • allkeys-random: 所有key通用; 隨機刪除一部分 key。
  • volatile-random: 只限於設置了 expire 的部分; 隨機刪除一部分 key。
  • volatile-ttl: 只限於設置了 expire 的部分; 優先刪除剩餘時間(time to live,TTL) 短的key。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章