1. 程式人生 > 資料庫 >面試專題-分散式快取-redis

面試專題-分散式快取-redis

1.Redis的應用場景

快取,毫無疑問這是Redis當今最為人熟知的使用場景。再提升伺服器效能方面非常有效;一些頻繁被訪問的資料,經常被訪問的資料如果放在關係型資料庫,每次查詢的開銷都會很大,而放在redis中,因為redis 是放在記憶體中的可以很高效的訪問

排行榜,在使用傳統的關係型資料庫(mysql oracle 等)來做這個事兒,非常的麻煩,而利用Redis的SortSet(有序集合)資料結構能夠簡單的搞定;

計算器/限速器,利用Redis中原子性的自增操作,我們可以統計類似使用者點贊數、使用者訪問數等,這類操作如果用MySQL,頻繁的讀寫會帶來相當大的壓力;限速器比較典型的使用場景是限制某個使用者訪問某個API的頻率,常用的有搶購時,防止使用者瘋狂點選帶來不必要的壓力;

好友關係,利用集合的一些命令,比如求交集、並集、差集等。可以方便搞定一些共同好友、共同愛好之類的功能;

簡單訊息佇列,除了Redis自身的釋出/訂閱模式,我們也可以利用List來實現一個佇列機制,比如:到貨通知、郵件傳送之類的需求,不需要高可靠,但是會帶來非常大的DB壓力,完全可以用List來完成非同步解耦;

Session共享,預設Session是儲存在伺服器的檔案中,如果是叢集服務,同一個使用者過來可能落在不同機器上,這就會導致使用者頻繁登陸;採用Redis儲存Session後,無論使用者落在那臺機器上都能夠獲取到對應的Session資訊。

2.Redis的不適合的應用場景

用Redis去儲存使用者的基本資訊,雖然它能夠支援持久化,但是它的持久化方案並不能保證資料絕對的落地,並且還可能帶來Redis效能下降,因為持久化太過頻繁會增大Redis服務的壓力。

總結就是資料量太大、資料訪問頻率非常低的業務都不適合使用Redis,資料太大會增加成本,訪問頻率太低,儲存在記憶體中純屬浪費資源。
在這裡插入圖片描述

3.使用redis的理由

面所說的一些場景, 快取可以用Memcache,Session共享能用MySql來實現,訊息佇列可以用RabbitMQ等,

理由
完全基於記憶體所以速度很快,使用C語言實現,網路層使用epoll解決高併發問題,單執行緒模型避免了不必要的上下文切換及競爭條件; 注意:單執行緒僅僅是說在網路請求這一模組上用一個請求處理客戶端的請求,在持久化時它就會重開一個執行緒/程序去進行處理

豐富的資料型別,常用的有 String、Hash、List、Set、 SortSet 這5種,都是基於鍵值的方式組織資料。每一種資料型別提供了非常豐富的操作命令,可以滿足絕大部分需求。

在這裡插入圖片描述
Redis的程式碼開源在GitHub,程式碼非常簡單優雅,願意花時間就能去看懂;它的編譯安裝也很簡單,不存在其他系統依賴;各種客戶端的語言支援也是非常完善。另外它還支援事務、持久化、主從複製讓高可用、分散式成為可能。

4.Redis的五種資料型別和適用的場景

String:做資料快取
Hash:購物車、單點登入、使用者資訊、登入資訊
List:評論、評分、回覆類資訊(有序可重)
Set:(無序不重)求幾個人之間的共同聯絡人,共同好友
Sorted set:最熱商品

這裡我相信99%的讀者都能回答上來Redis的5個基本資料型別。如果回答不出來的小夥伴我們就要加油補課喲,大家知道五種型別最適合的場景更好。

5.Redis常用的命令

keys pattern
*表示區配所有
以bit開頭的

檢視Exists  key是否存在

Set
設定 key 對應的值為 string 型別的 value。

setnx
設定 key 對應的值為 string 型別的 value。如果 key 已經存在,返回 0,nx 是 not exist 的意思。

刪除某個key
第一次返回1 刪除了 第二次返回0

Expire 設定過期時間(單位秒)

TTL檢視剩下多少時間
返回負數則key失效,key不存在了

Setex
設定 key 對應的值為 string 型別的 value,並指定此鍵值對應的有效期。

Mset
一次設定多個 key 的值,成功返回 ok 表示所有的值都設定了,失敗返回 0 表示沒有任何值被設定。

Getset
設定 key 的值,並返回 key 的舊值。

Mget
一次獲取多個 key 的值,如果對應 key 不存在,則對應返回 nil。

Incr
對 key 的值做加加操作,並返回新的值。注意 incr 一個不是 int 的 value 會返回錯誤,incr 一個不存在的 key,則設定 
key 為 1

incrby
同 incr 類似,加指定值 ,key 不存在時候會設定 key,並認為原來的 value 是 0

Decr
對 key 的值做的是減減操作,decr 一個不存在 key,則設定 key 為-1

Decrby
同 decr,減指定值。

Append
給指定 key 的字串值追加 value,返回新字串值的長度。

Strlen
取指定 key 的 value 值的長度。

persist xxx(取消過期時間)
選擇資料庫(0-15庫)
Select 0 //選擇資料庫

move age 1//把age 移動到1庫

Randomkey隨機返回一個key

Rename重新命名

Type 返回資料型別

6.是否使用過Redis叢集,叢集的高可用怎麼保證,叢集的原理是什麼?

哨兵
Redis Sentinal著眼於高可用,在master宕機時會自動將slave提升為master,繼續提供服務。
在這裡插入圖片描述
Redis sentinel 是一個分散式系統中監控 redis 主從伺服器,並在主伺服器下線時自動進行故障轉移。其中三個特性:

監控(Monitoring): Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。

提醒(Notification): 當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程式傳送通知。

自動故障遷移(Automatic failover): 當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作。

特點:

1、保證高可用
2、監控各個節點
3、自動故障遷移

缺點:

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

直連型

Redis Cluster著眼於擴充套件性,在單個redis記憶體不足時,使用Cluster進行分片儲存。
在這裡插入圖片描述從redis 3.0之後版本支援redis-cluster叢集,Redis-Cluster採用無中心結構,每個節點儲存資料和整個叢集狀態,每個節點都和其他所有節點連線。

特點:

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

缺點:

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

7、為什麼Redis需要把所有資料放到記憶體中?

Redis為了達到最快的讀寫速度將資料都讀到記憶體中,並通過非同步的方式將資料寫入磁碟。
所以redis具有快速和資料持久化的特徵。如果不將資料放在記憶體中,磁碟I/O速度為嚴重影響redis的效能。
在記憶體越來越便宜的今天,redis將會越來越受歡迎。 如果設定了最大使用的記憶體,則資料已有記錄數達到記憶體限值後不能繼續插入新值。

8.為什麼專案中需要用redis快取?

因為專案首頁是一個網站門戶,在高併發下,需要進行經常被瀏覽!首頁上的資料是來源於資料庫的,由於是併發訪問,如果資料全部查詢資料庫,那麼對資料庫的壓力很大,有可能把資料庫壓垮!及時資料庫查詢沒問題,但是查詢速度肯定會很慢,影響網站的載入,給使用者不好的體驗!所以需要加快取,提高資料查詢速度。

9.什麼資料加快取?

首頁變化頻率不高的資料,需要加快取!例如:首頁商品分類,廣告資料。放入快取中,如果查詢的時候發現快取中有,那麼查詢快取。如果說快取中沒有,那麼查詢資料庫,查詢完資料庫,再放入快取中一份,下次查詢快取!

10.為什使用Redis做快取,而不是用MyBatis內建的快取的?

MyBatis是JVM的快取,JVM快取比Redis還快!

mybatis的快取:分為一級快取和二級快取,一級快取的作用範圍為session,所以當session commit或close後,快取就會被清空 ,二級快取的作用範圍為sqlsessionfactory,對映語句檔案中的所有select語句都會被快取,所有CRUD的操作都會重新整理快取,快取會儲存1024個物件,快取容易造成髒毒資料,影響真實資料的準確性,實際開發業務中會放棄二級快取。

redis的快取:可控制的後端快取服務,通常用來快取後端資料,當程式第二次訪問資料庫的時候,命中redis,大大減少資料庫的負擔,減少訪問資料庫的連結時間,實際開發過程中都會採用這種快取方式,達到訪問速度和效率的解決方案。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-dCkpTqDV-1606316833602)(%E7%94%A8redis%E7%BC%93%E5%AD%98%EF%BC%9F.assets/1606187536266.png)]

Redis分散式快取,Redis快取可以跨JVM虛擬機器。但是MyBatis是基於JVM,不能跨虛擬機器!MyBatis快取在分散式環境下,不方便快取管理!

Redis是分散式快取,可以給多個伺服器提供快取支援,集中化管理快取!

Redis作用不僅僅是用作快取,還可以實現秒殺、分散式鎖!

11.快取是什麼型別、什麼格式

選型:字串型別

格式:JSON     物件-----JSON  JSON--物件

API:Spring Data  Redis

12.如何配置快取

 //JSON轉換器
     //轉換器
        Jackson2JsonRedisSerializer<List> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(List.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jsonRedisSerializer.setObjectMapper(om);
        //設定Jackson轉換器
        redisTemplate.setValueSerializer(jsonRedisSerializer);

        //為key設定序列化器 自動把物件序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        //為value設定序列化器
        //template.setValueSerializer(new StringRedisSerializer());

        List<Product> products_1233=(List<Product>)this.redisTemplate.opsForValue().get("products_1233");
        if(products_1233==null){
            log.info("查詢資料庫首頁的分類資料,查詢完畢後快取中放一份.....");
            products_1233 = productServiceApi.findProductsByCategory(1233);
            this.redisTemplate.opsForValue().set("products_1233", products_1233);
        }else{
            log.info("查詢Redis快取的分類資料.....");
        }

13.分散式專案下快取一致性問題

問題來源: 由於添加了快取,redis其實就是資料庫中資料一個備份,查詢Redis比查詢資料庫要快!但是快取一旦有了之後,會有一個問題,關於快取何時重新整理,保證資料庫中的資料庫和快取一致性!

如果資料庫發生了修改,那麼快取還是之前的資料,就會造成使用者瀏覽的髒資料!

方法1(快取實時同步): 強一致性

當後臺商城管理員增刪改資料庫的時候,呼叫一下Redis API 把快取同步一下(查詢一下資料庫最新的修改之後的資料把redis中的老的資料覆蓋一下)。

再清除快取,先更新、刪除、新增、資料庫。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-UVU5IiBI-1606316833603)(%E7%94%A8redis%E7%BC%93%E5%AD%98%EF%BC%9F.assets/1606202804027.png)]

方法2(快取非實時同步):弱一致性 最終一致性

把redis中的key設定一個過期,不要一直快取,每間隔一段時間,快取自動失效!快取失效,預設就查詢一次資料庫,下次又走快取!!

快取設定過期時間,保證最終一致性。

方法3(快取非實時同步):弱一致性 最終一致性

專案後臺開啟一個定時任務,每間隔一段時間,把快取同步一下,保證資料庫中的資料和快取一致性!

14.快取生效時機

專案啟動之後,第一次請求,預設查詢資料庫,以後的請求先去查詢快取,快取沒有失效那麼取快取,快取如果失效,查詢資料庫,然後同步快取一份。

15.快取預熱

在Web應用啟動的時候,事先往快取中放一份,當請求訪問的時候,快取已經有資料了!快取生效的時機提前啦!

ApplicationRunner
實現ApplicationRunner介面
打上@Component+implements ApplicationRunner

@Component
public class DemoApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("這裡進行快取預熱");
    }
}

16. 快取穿透

16.1.概念

快取穿透:快取和資料庫中都沒有的資料,可使用者還是源源不斷的發起請求,導致每次請求都會到資料庫,從而壓垮資料庫。

如下圖紅色的流程:
在這裡插入圖片描述

比如客戶查詢一個根本不存在的東西,首先從Redis中查不到,然後會去資料庫中查詢,資料庫中也查詢不到,那麼就不會將資料放入到快取中,後面如果還有類似源源不斷的請求,最後都會壓到資料庫來處理,從而給資料庫造成巨大的壓力。

16.2.解決辦法

①、業務層校驗

使用者發過來的請求,根據請求引數進行校驗,對於明顯錯誤的引數,直接攔截返回。

比如,請求引數為主鍵自增id,那麼對於請求小於0的id引數,明顯不符合,可以直接返回錯誤請求。

②、不存在資料設定短過期時間

對於某個查詢為空的資料,可以將這個空結果進行Redis快取,但是設定很短的過期時間,比如30s,可以根據實際業務設定。注意一定不要影響正常業務。

③、布隆過濾器

關於布隆過濾器,後面會詳細介紹。布隆過濾器是一種資料結構,利用極小的記憶體,可以判斷大量的資料“一定不存在或者可能存在”。

對於快取擊穿,我們可以將查詢的資料條件都雜湊到一個足夠大的布隆過濾器中,使用者傳送的請求會先被布隆過濾器攔截,一定不存在的資料就直接攔截返回了,從而避免下一步對資料庫的壓力。

17、快取擊穿

17.1.概念

快取擊穿:Redis中一個熱點key在失效的同時,大量的請求過來,從而會全部到達資料庫,壓垮資料庫。
在這裡插入圖片描述

這裡要注意的是這是某一個熱點key過期失效,和後面介紹快取雪崩是有區別的。比如淘寶雙十一,對於某個特價熱門的商品資訊,快取在Redis中,剛好0點,這個商品資訊在Redis中過期查不到了,這時候大量的使用者又同時正好訪問這個商品,就會造成大量的請求同時到達資料庫。

17.2.解決辦法

①、設定熱點資料永不過期

對於某個需要頻繁獲取的資訊,快取在Redis中,並設定其永不過期。當然這種方式比較粗暴,對於某些業務場景是不適合的。

強一致性 增刪改資料庫中資料庫的時候,快取同步!

②、定時更新

比如這個熱點資料的過期時間是1h,那麼每到59minutes時,通過定時任務去更新這個熱點key,並重新設定其過期時間。

③、互斥鎖

這是解決快取擊穿比較常用的方法。

互斥鎖簡單來說就是在Redis中根據key獲得的value值為空時,先鎖上,然後從資料庫載入,載入完畢,釋放鎖。若其他執行緒也在請求該key時,發現獲取鎖失敗,則睡眠一段時間(比如100ms)後重試。

18、快取雪崩

18.1.概念

快取雪崩:Redis中快取的資料大面積同時失效,或者Redis宕機,從而會導致大量請求直接到資料庫,壓垮資料庫。
在這裡插入圖片描述
對於一個業務系統,如果Redis宕機或大面積的key同時過期,會導致大量請求同時打到資料庫,這是災難性的問題。

18.2.解決辦法

①、設定有效期均勻分佈

避免快取設定相近的有效期,我們可以在設定有效期時增加隨機值;

或者統一規劃有效期,使得過期時間均勻分佈。

②、資料預熱

對於即將來臨的大量請求,我們可以提前走一遍系統,將資料提前快取在Redis中,並設定不同的過期時間

③、保證Redis服務高可用

前面我們介紹過Redis的哨兵模式和叢集模式,為防止Redis叢集單節點故障,可以通過這兩種模式實現高可用。

19.Redis為什麼要有持久化?

由於Redis是一個基於記憶體的資料庫,因此資料容易丟失,為了解決資料丟失的問題,於是Redis就設計了持久化

20.Redis的持久的方式有哪些?

有兩個,分別是rdb和aof的持久化方式

21.分別介紹一下Redis的持久化方式的機制

rdb持久化:Redis為我們提供了一個rdb的持久化方式具體每隔一定時間,或者當key的改變達到一定的數量的時候,就會自動往磁碟儲存一次

1 save 900 1(900秒後1個key改變) 2 save 300 10(300秒後19個key改變) 3 save 60 10000(60秒後10000個key改變)(save是或者的關係)

aof持久化:Redis還為我們提供了一個aof的持久化方式,這種方式是通過記錄使用者的操作過程(使用者每執行一次命令,就會被Redis記錄在XXX.aof檔案裡,如果突然斷電了,Redis的資料就會通過重新讀取並執行XXX.aof裡的命令記錄來恢復資料)來恢復資料

22.兩種Redis持久化方式的優缺點

都是為了防止資料丟失

rdb:假如三種方式沒有一種被滿足,觸發不了儲存,突然斷電,那就會丟失資料

aof:為了解決rdb的弊端,就有了aof的持久化,始終在一個檔案裡儲存記錄,但aof的持久化隨著時間的推移資料量會越來越大,佔用很大的空間

23.Redis支援哪幾種資料型別?

支援多種型別的資料結構

1.string:最基本的資料型別,二進位制安全的字串,最大512M。

2.list:按照新增順序保持順序的字串列表。

3.set:無序的字串集合,不存在重複的元素。

4.sorted set:已排序的字串集合。

5.hash:key-value對的一種集合。

在這裡插入圖片描述

24.Redis是單程序單執行緒的?

Redis是單程序單執行緒的,Redis利用佇列技術將併發訪問變為序列訪問,消除了傳統資料庫序列控制的開銷。

25.Redis為什麼是單執行緒的?

多執行緒處理會涉及到鎖,而且多執行緒處理會涉及到執行緒切換而消耗CPU。因為CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器記憶體或者網路頻寬。單執行緒無法發揮多核CPU效能,不過可以通過在單機開多個Redis例項來解決。

26.Redis單點吞吐量

單點TPS達到8萬/秒,QPS達到10萬/秒,補充下TPS和QPS的概念

1.QPS: 應用系統每秒鐘最大能接受的使用者訪問量

每秒鐘處理完請求的次數,注意這裡是處理完,具體是指發出請求到伺服器處理完成功返回結果。可以理解在server中有個counter,每處理一個請求加1,1秒後counter=QPS。

2.TPS: 每秒鐘最大能處理的請求數

每秒鐘處理完的事務次數,一個應用系統1s能完成多少事務處理,一個事務在分散式處理中,可能會對應多個請求,對於衡量單個介面服務的處理能力,用QPS比較合理。

27.Redis有哪幾種資料淘汰策略?

在Redis中,允許使用者設定最大使用記憶體大小server.maxmemory,當Redis 記憶體資料集大小上升到一定大小的時候,就會施行資料淘汰策略。

1.volatile-lru:從已設定過期的資料集中挑選最近最少使用的淘汰

2.volatile-ttr:從已設定過期的資料集中挑選將要過期的資料淘汰

3.volatile-random:從已設定過期的資料集中任意挑選資料淘汰

4.allkeys-lru:從資料集中挑選最近最少使用的資料淘汰

5.allkeys-random:從資料集中任意挑選資料淘汰

6.noenviction:禁止淘汰資料

28.說說Redis雜湊槽的概念?

Redis叢集沒有使用一致性hash,而是引入了雜湊槽的概念,Redis叢集有16384個雜湊槽,每個key通過CRC16校驗後對16384取模來決定放置哪個槽,叢集的每個節點負責一部分hash槽。