天天看點

redis面試~~有這一篇就夠了

redis介紹

1、redis 是什麼?

redis可以了解就是一個資料庫,不過與傳統資料庫不同的是 redis 的資料是存在記憶體中的,是以讀寫速度非常快,是以 redis 被廣泛應用于緩存方向。另外,redis 也經常用來做分布式鎖。redis 提供了多種資料類型來支援不同的業務場景。除此之外,redis 支援事務 、持久化、LUA腳本、LRU驅動事件、多種叢集方案。

2、為什麼要用 redis?/為什麼要用緩存?

因為傳統的關系型資料庫如Mysql已經不能适用所有的場景了,比如秒殺的庫存扣減,APP首頁的通路流量高峰等等,都很容易把資料庫打崩,是以引入了緩存中間件,目前市面上比較常用的緩存中間件有 Redis 和 Memcached。

3、為什麼要用 redis 而不用 map/guava 做緩存?

緩存分為本地緩存和分布式緩存。以 Java 為例,使用自帶的 map 或者 guava 實作的是本地緩存,最主要的特點是輕量以及快速,生命周期随着 jvm 的銷毀而結束。

使用 redis 或 memcached 之類的稱為分布式緩存,在多執行個體的情況下,各執行個體共用一份緩存資料,緩存具有一緻性。

缺點是需要保持 redis 或 memcached服務的高可用,整個程式架構上較為複雜。

4、Memcache 和 redis 對比

1、redis支援更豐富的資料類型(支援更複雜的應用場景):Redis不僅僅支援簡單的k/v類型的資料,同時還提供list,set,zset,hash等資料結構的存儲。memcache支援簡單的資料類型,String。

2、Redis支援資料的持久化,可以将記憶體中的資料保持在磁盤中,重新開機的時候可以再次加載進行使用,而Memecache把資料全部存在記憶體之中。

3、叢集模式:memcached沒有原生的叢集模式,需要依靠用戶端來實作往叢集中分片寫入資料;但是 redis 目前是原生支援 cluster 模式的.

4、Memcached是多線程,非阻塞IO複用的網絡模型;Redis使用單線程的多路 IO 複用模型。

5、redis 的線程模型

redis 内部使用檔案事件處理器 file event handler,這個檔案事件處理器是單線程的,是以 redis 才叫做單線程的模型。它采用 IO 多路複用機制同時監聽多個 socket,根據 socket 上的事件來選擇對應的事件處理器進行處理。 檔案事件處理器的結構包含 4 個部分:

1、多個 socket. 2、IO 多路複用程式. 3、檔案事件分派器. 4、事件處理器(連接配接應答處理器、指令請求處理器、指令回複處理器)

6、Redis 為什麼是單線程的?

1、一直可能有個誤區,多線程 一定比 單線程 效率高,其實不然!多線程就涉及到上線文切換,CPU上下文的切換大概在 1500ns 左右。

2、redis 核心就是 如果我的資料全都在記憶體裡,我單線程的去操作 就是效率最高的,為什麼呢?因為多線程的本質就是 CPU 模拟出來多個線程的情況,這種模拟出來的情況就有一個代價,就是上下文的切換,對于一個記憶體的系統來說,它沒有上下文的切換就是效率最高的。

7、redis的優缺點

優點:

1、支援多種資料類型 string、list、set、zset、hash

2、資料可以持久化保持(AOF、快照),寫入硬碟,

3、支援災難恢複,主從複制。主機會自動将資料同步到從機,可以進行讀寫分離。

4、讀寫性能優異。

缺點:

1、redis不支援自動容錯和恢複功能,主從當機都會導緻前端讀寫失敗,需要手動重新開機機器。

2、主機當機,主從資料複制過程中,資料未完全複制到從機,會出現資料不一緻。

3、redis較難支援線上擴容,當叢集資料達到上限線上擴容變得複雜。

8、redis為什麼快?

Redis采用的是基于記憶體的采用的是單程序單線程模型的 KV 資料庫,由C語言編寫,官方提供的資料是可以達到100000+的QPS(每秒内查詢次數)。

1、完全基于記憶體,絕大部分請求是純粹的記憶體操作,非常快速。它的,資料存在記憶體中,類似于HashMap,HashMap的優勢 就是查找和操作的時間複雜度都是O(1);

2、資料結構簡單,對資料操作也簡單,Redis中的資料結構是專門進行設計的;

3、采用單線程,避免了不必要的上下文切換和競争條件,也不存在多程序或者多線程導緻的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因為可能出現死鎖而導緻的性能消耗;

4、使用多路I/O複用模型,非阻塞IO;

5、使用底層模型不同,它們之間底層實作方式以及與用戶端之間通信的應用協定不一樣,Redis直接自己建構了VM 機制 ,因為一般的系統調用系統函數的話,會浪費一定的時間去移動和請求

redis支援的資料結構

五種基本資料結構

String字元串類型

介紹

String 類型是 Redis 中最常使用的類型,内部的實作是通過 SDS(Simple Dynamic String )來存儲的。SDS 類似于 Java 中的 ArrayList,可以通過預配置設定備援空間的方式來減少記憶體的頻繁配置設定。這是最簡單的類型,就是普通的 set 和 get,做簡單的 KV 緩存。

應用場景

1、緩存功能:String字元串是最常用的資料類型,不僅僅是Redis,各個語言都是最基本類型,是以,利用Redis作為緩存,配合其它資料庫作為存儲層,利用Redis支援高并發的特點,可以大大加快系統的讀寫速度、以及降低後端資料庫的壓力。

2、計數器:許多系統都會使用Redis作為系統的實時計數器,可以快速實作計數和查詢的功能。比如想知道什麼時候封鎖一個IP位址(通路超過幾次),INCRBY指令讓這些變得很容易,通過原子遞增保持計數。

3、共享使用者Session:使用者重新重新整理一次界面,可能需要通路一下資料進行重新登入,或者通路頁面緩存Cookie,但是可以利用Redis将使用者的Session集中管理,在這種模式隻需要保證Redis的高可用,每次使用者Session的更新和擷取都可以快速完成。大大提高效率。

List清單類型

介紹

Redis中的List其實就是連結清單(Redis用雙端連結清單實作List)。使用List結構,我們可以輕松地實作最新消息排隊功能(比如新浪微網誌的TimeLine)。List的另一個應用就是消息隊列,可以利用List的 PUSH 操作,将任務存放在List中,然後工作線程再用 POP 操作将任務取出進行執行。

應用場景

1、消息隊列:Redis的連結清單結構,可以輕松實作阻塞隊列,可以使用左進右出的指令組成來完成隊列的設計。比如:資料的生産者可以通過Lpush指令從左邊插入資料,多個資料消費者,可以使用BRpop指令阻塞的“搶”清單尾部的資料。

2、文章清單或者資料分頁展示的應用。比如,我們常用的部落格網站的文章清單,當使用者量越來越多時,而且每一個使用者都有自己的文章清單,而且當文章多時,都需要分頁展示,這時可以考慮使用Redis的清單,清單不但有序同時還支援按照範圍内擷取元素,可以完美解決分頁查詢功能。大大提高查詢效率

Set集合類型

介紹

Redis的集合類型和清單都可以存儲多個字元串,它們的不同之處在于。清單可以存儲多個相同的字元串,而集合通過散清單來保證自己存儲的每個字元串都是各不相同的。

應用場景

1、共同好友、二度好友(并集、交集、差集運算)

2、利用唯一性,可以統計通路網站的所有獨立IP

Hash散列類型

介紹

Redis的散列可以存儲多個鍵值對之間的映射。和字元串一樣,散列存儲的值既可以是字元串又可以是數字值。并且使用者同樣可以對散列存儲的數字進行自增或自減操作。

應用場景

适用于存儲對象(object)資訊,如使用者詳細資訊,使用者id為key,他的姓名、性别、年齡等資訊為field-value。存儲多個使用者資訊的化,可以将filed存為使用者id,每個使用者的資訊序列化為json存于value。

有序集合Sorted set

介紹

Sorted set 是排序的 Set,去重但可以排序,寫進去的時候給一個分數,自動根據分數排序。有序集合的使用場景與集合類似,但是set集合不是自動有序的,而Sorted set可以利用分數進行成員間的排序,而且是插入時就排序好。是以當你需要一個有序且不重複的集合清單時,就可以選擇Sorted set資料結構作為選擇方案。

應用場景

1、排行榜:有序集合經典使用場景。例如視訊網站需要對使用者上傳的視訊做排行榜,榜單維護可能是多方面:按照時間、按照播放量、按照獲得的贊數等。

2、用Sorted Sets來做帶權重的隊列,比如普通消息的score為1,重要消息的score為2,然後工作線程可以選擇按score的倒序來擷取工作任務。讓重要的任務優先執行。

redis資料結構-進階篇

Bitmap :位圖是支援按 bit 位來存儲資訊,可以用來實作 布隆過濾器(BloomFilter);

HyperLogLog:供不精确的去重計數功能,比較适合用來做大規模資料的去重統計,例如統計 UV;

Geospatial:可以用來儲存地理位置,并作位置距離計算或者根據半徑計算位置等。比如用Redis來實作附近的人?或者計算最優地圖路徑?

pub/sub:功能是訂閱釋出功能,可以用作簡單的消息隊列。

Pipeline:可以批量執行一組指令,一次性傳回全部結果,可以減少頻繁的請求應答。

Lua:Redis 支援送出 Lua 腳本來執行一系列的功能。

擴充-Bloom Filter

1、背景如果想判斷一個元素是不是在一個集合裡,一般想到的是将集合中所有元素儲存起來,然後通過比較确定。連結清單、樹、散清單(又叫哈希表,Hash table)等等資料結構都是這種思路,存儲位置要麼是磁盤,要麼是記憶體。很多時候要麼是以時間換空間,要麼是以空間換時間。在響應時間要求比較嚴格的情況下,如果我們存在内裡,那麼随着集合中元素的增加,我們需要的存儲空間越來越大,以及檢索的時間越來越長,導緻記憶體開銷太大、時間效率變低。此時需要考慮解決的問題就是,在資料量比較大的情況下,既滿足時間要求,又滿足空間的要求。即我們需要一個時間和空間消耗都比較小的資料結構和算法。Bloom Filter就是一種解決方案。

2、Bloom Filter 原理布隆過濾器的原理是,當一個元素被加入集合時,通過K個散列函數将這個元素映射成一個位數組中的K個點,把它們置為1。檢索時,我們隻要看看這些點是不是都是1就(大約)知道集合中有沒有它了:如果這些點有任何一個0,則被檢元素一定不在;如果都是1,則被檢元素很可能在。這就是布隆過濾器的基本思想。Bloom Filter跟單哈希函數Bit-Map不同之處在于:Bloom Filter使用了k個哈希函數,每個字元串跟k個bit對應。進而降低了沖突的機率。

3、緩存穿透每次查詢都會直接打到DB,言而簡之就是我們先把我們資料庫的資料都加載到我們的過濾器中,比如資料庫的id現在有:1、2、3那就用id:1 為例子他在上圖中經過三次hash之後,把三次原本值0的地方改為1下次資料進來查詢的時候如果id的值是1,那麼我就把1拿去三次hash 發現三次hash的值,跟上面的三個位置完全一樣,那就能證明過濾器中有1的,反之如果不一樣就說明不存在了。一般我們都會用來防止緩存擊穿。一般你資料庫的id都是1開始然後自增的,那我知道你接口是通過id查詢的,我就拿負數去查詢,這個時候,會發現緩存裡面沒這個資料,我又去資料庫查也沒有,一個請求這樣,100個,1000個,10000個呢?你的DB基本上就扛不住了,如果在緩存裡面加上這個,是不是就不存在了,你判斷沒這個資料就不去查了,直接return一個資料為空不就好了嘛。

4、Bloom Filter的缺點bloom filter之是以能做到在時間和空間上的效率比較高,是因為犧牲了判斷的準确率、删除的便利性存在誤判,可能要查到的元素并沒有在容器中,但是hash之後得到的k個位置上值都是1。如果bloom filter中存儲的是黑名單,那麼可以通過建立一個白名單來存儲可能會誤判的元素。删除困難。一個放入容器的元素映射到bit數組的k個位置上是1,删除的時候不能簡單的直接置為0,可能會影響其他元素的判斷。可以采用Counting Bloom Filter

5、Bloom Filter 實作布隆過濾器有許多實作與優化,Guava中就提供了一種Bloom Filter的實作。在使用bloom filter時,繞不過的兩點是預估資料量n以及期望的誤判率fpp,在實作bloom filter時,繞不過的兩點就是hash函數的選取以及bit數組的大小。對于一個确定的場景,我們預估要存的資料量為n,期望的誤判率為fpp,然後需要計算我們需要的Bit數組的大小m,以及hash函數的個數k,并選擇hash函數。

redis 事務

1 Redis事務的概念: Redis 提供的不是嚴格的事務,Redis 隻保證串行執行指令,并且能保證全部執行,但是執行指令失敗時并不會復原,而是會繼續執行下去。

2 Redis事務沒有隔離級别的概念: 批量操作在發送 EXEC 指令前被放入隊列緩存,并不會被實際執行,也就不存在事務内的查詢要看到事務裡的更新,事務外查詢不能看到。

3 Redis不保證原子性: Redis中,單條指令是原子性執行的,但事務不保證原子性,且沒有復原。事務中任意指令執行失敗,其餘的指令仍會被執行。

4 Redis事務的三個階段:開始事務指令入隊執行事務

5 Redis事務相關指令: watch key1 key2 … : 監視一或多個key,如果在事務執行之前,被監視的key被其他指令改動,則事務被打斷 ( 類似樂觀鎖 ) multi : 标記一個事務塊的開始( queued ) exec : 執行所有事務塊的指令 ( 一旦執行exec後,之前加的監控鎖都會被取消掉 ) discard : 取消事務,放棄事務塊中的所有指令 unwatch : 取消watch對所有key的監控

redis-持久化

持久化的實作方式

1、快照方式持久化快照方式持久化就是在某時刻把所有資料進行完整備份。例:Mysql的Dump方式、Redis的RDB方式。

2、寫日志方式持久化寫日志方式持久化就是把使用者執行的所有寫指令(增删改)備份到檔案中,還原資料時隻需要把備份的所有指令重新執行一遍即可。例:Mysql的Binlog、Redis的AOF、Hbase的HLog。

兩種機制的對比

RDB

優點:他會生成多個資料檔案,每個資料檔案分别都代表了某一時刻Redis裡面的資料,這種方式,有沒有覺得很适合做冷備,完整的資料運維設定定時任務,定時同步到遠端的伺服器,比如阿裡的雲服務,這樣一旦線上挂了,你想恢複多少分鐘之前的資料,就去遠端拷貝一份之前的資料就好了。RDB對Redis的性能影響非常小,是因為在同步資料的時候他隻是fork了一個子程序去做持久化的,而且他在資料恢複的時候速度比AOF來的快。

缺點:RDB都是快照檔案,都是預設五分鐘甚至更久的時間才會生成一次,這意味着你這次同步到下次同步這中間五分鐘的資料都很可能全部丢失掉。AOF則最多丢一秒的資料,資料完整性上高下立判。還有就是RDB在生成資料快照的時候,如果檔案很大,用戶端可能會暫停幾毫秒甚至幾秒,你公司在做秒殺的時候他剛好在這個時候fork了一個子程序去生成一個大快照,哦豁,出大問題。

AOF

優點:上面提到了,RDB五分鐘一次生成快照,但是AOF是一秒一次去通過一個背景的線程fsync操作,那最多丢這一秒的資料。AOF在對日志檔案進行操作的時候是以append-only的方式去寫的,他隻是追加的方式寫資料,自然就少了很多磁盤尋址的開銷了,寫入性能驚人,檔案也不容易破損。AOF的日志是通過一個叫非常可讀的方式記錄的,這樣的特性就适合做災難性資料誤删除的緊急恢複了,比如公司的實習生通過flushall清空了所有的資料,隻要這個時候背景重寫還沒發生,你馬上拷貝一份AOF日志檔案,把最後一條flushall指令删了就完事了。

缺點:一樣的資料,AOF檔案比RDB還要大。AOF開啟後,Redis支援寫的QPS會比RDB支援寫的要低,他不是每秒都要去異步重新整理一次日志嘛fsync,當然即使這樣性能還是很高,我記得ElasticSearch也是這樣的,異步重新整理緩存區的資料去持久化,為啥這麼做呢,不直接來一條怼一條呢,那我會告訴你這樣性能可能低到沒辦法用的,大家可以思考下為啥喲。

如何選擇使用哪種持久化方式?

一般來說, 如果想達到足以媲美 PostgreSQL 的資料安全性, 你應該同時使用兩種持久化功能。如果你非常關心你的資料, 但仍然可以承受數分鐘以内的資料丢失, 那麼你可以隻使用 RDB 持久化。有很多使用者都隻使用 AOF 持久化, 但并不推薦這種方式: 因為定時生成 RDB 快照(snapshot)非常便于進行資料庫備份, 并且 RDB 恢複資料集的速度也要比 AOF 恢複的速度要快。

redis-主從複制

什麼是主從複制

Redis的主從複制機制是指可以讓從伺服器(slave)能精确複制主伺服器(master)的資料

主從複制的方式

Redis的主從複制是異步複制,異步分為兩個方面,

一個是master伺服器在将資料同步到slave時是異步的,是以master伺服器在這裡仍然可以接收其他請求,

一個是slave在接收同步資料也是異步的。

複制方式Redis主從複制分為以下三種方式:

一、當master伺服器與slave伺服器正常連接配接時,master伺服器會發送資料指令流給slave伺服器,将自身資料的改變複制到slave伺服器。二、當因為各種原因master伺服器與slave伺服器斷開後,slave伺服器在重新連上master伺服器時會嘗試重新擷取斷開後未同步的資料即部分同步,或者稱為部分複制。

三、如果無法部分同步(比如初次同步),則會請求進行全量同步,這時master伺服器會将自己的rdb檔案發送給slave伺服器進行資料同步,并記錄同步期間的其他寫入,再發送給slave伺服器,以達到完全同步的目的,這種方式稱為全量複制。

主從複制的原理

master伺服器會記錄一個replicationId的僞随機字元串,用于辨別目前的資料集版本,還會記錄一個當資料集的偏移量offset,不管master是否有配置slave伺服器,replication Id和offset會一直記錄并成對存在。

當master與slave正常連接配接時,slave使用PSYNC指令向master發送自己記錄的舊master的replication id和offset,而master會計算與slave之間的資料偏移量,并将緩沖區中的偏移數量同步到slave,此時master和slave的資料一緻。而如果slave引用的replication太舊了,master與slave之間的資料差異太大,則master與slave之間會使用全量複制的進行資料同步。

如何避免slave被清空

slave會被清空?slave不用同步了master的資料嗎?備份的資料怎麼會清空了呢?當master伺服器關閉了持久化時,如果發生故障後自動重新開機時,由本地沒有儲存持久化的資料,重新開機的Redis記憶體資料為空,而slave會自動同步master的資料,這時候,slave伺服器的資料也會被清空。如何避免slave被清空呢?如果條件允許(一般都可以的),master伺服器還是要開啟持久化,這樣master故障重新開機時,可以快速恢複資料,而同步這台master的slave資料也不會被清空。如果master不能開啟持久化,則不應該設定讓master發生故障後重新開機(有些機器會配置自動重新開機),而是将某個slave伺服器更新為master伺服器,對外繼續提供服務。

主從複制中的key過期問題

我們都知道Redis可以通過設定key的過期時間來限制key的生存時間,Redis處理key過期有惰性删除和定期删除兩種機制,而在配置主從複制後,slave伺服器就沒有權限處理過期的key,這樣的話,對于在master上過期的key,在slave伺服器就可能被讀取,是以master會累積過期的key,積累一定的量之後,發送del指令到slave,删除slave上的key。如果slave伺服器更新為master伺服器 ,則它将開始獨立地計算key過期時間,而不需要通過master伺服器的幫助。

主從複制的作用

1 儲存Redis資料副本當我們隻是通過RDB或AOF把Redis的記憶體資料持久化畢竟隻是在本地,并不能保證絕對的安全,而通過将資料同步slave伺服器上,可以保留多一個資料備份,更好地保證資料的安全。

2 讀寫分離單機QPS是有上限的,而且Redis的特性就是必須支撐讀高并發的,那你一台機器又讀又寫,這誰頂得住啊,不當人啊!但是你讓這個master機器去寫,資料同步給别的slave機器,他們都拿去讀,分發掉大量的請求那是不是好很多,而且擴容的時候還可以輕松實作水準擴容。啟動一台slave 的時候,他會發送一個psync指令給master ,如果是這個slave第一次連接配接到master,他會觸發一個全量複制。master就會啟動一個線程,生成RDB快照,還會把新的寫請求都緩存在記憶體中,RDB檔案生成後,master會将這個RDB發送給slave的,slave拿到之後做的第一件事情就是寫進本地的磁盤,然後加載進記憶體,然後master會把記憶體裡面緩存的那些新命名都發給slave。

3 高可用性與故障轉移伺服器的高可用性是指伺服器能提供7*24小時不間斷的服務,Redis可以通過Sentinel系統管理多個Redis伺服器,當master伺服器發生故障時,Sentineal系統會根據一定的規則将某台slave伺服器更新為master伺服器,繼續提供服務,實作故障轉移,保證Redis服務不間斷。

Sentinel哨兵元件的主要功能

叢集監控:負責監控 Redis master 和 slave 程序是否正常工作。消息通知:如果某個 Redis 執行個體有故障,那麼哨兵負責發送消息作為報警通知給管理者。故障轉移:如果 master node 挂掉了,會自動轉移到 slave node 上。配置中心:如果故障轉移發生了,通知 client 用戶端新的 master 位址。

面試題

1、redis的基礎知識

2、redis記憶體淘汰機制

Redis的過期政策,是有定期删除+惰性删除兩種。

volatile-lru:從已設定過期時間的資料集(server. db[i]. expires)中挑選最近最少使用的資料淘汰。

volatile-ttl:從已設定過期時間的資料集(server. db[i]. expires)中挑選将要過期的資料淘汰。

volatile-random:從已設定過期時間的資料集(server. db[i]. expires)中任意選擇資料淘汰。

allkeys-lru:從資料集(server. db[i]. dict)中挑選最近最少使用的資料淘汰。

allkeys-random:從資料集(server. db[i]. dict)中任意選擇資料淘汰。no-enviction(驅逐):禁止驅逐資料。

3、主從複制時,資料傳輸的時候斷網了或者伺服器挂了怎麼辦啊?

傳輸過程中有什麼網絡問題啥的,會自動重連的,并且連接配接之後會把缺少的資料補上的。需要注意的就是,RDB快照的資料生成的時候,緩存區也必須同時開始接受新請求,不然你舊的資料過去了,你在同步期間的增量資料咋辦?

4、redis-緩存雪崩、擊穿、穿透

雪崩

同一時間所有的redis全部失效,所有的請求都打到資料庫。如何解決?處理緩存雪崩簡單,在批量往Redis存資料的時候,把每個Key的失效時間都加個随機值就好了,這樣可以保證資料不會在同一時間大面積失效,或者設定熱點資料永遠不過期,有更新操作就更新緩存就好了(比如運維更新了首頁商品,那你刷下緩存就完事了,不要設定過期時間)。

什麼是緩存穿透和擊穿?它們跟雪崩的差別?

緩存穿透:是指緩存和資料庫中都沒有的資料,而使用者不斷發起請求,我們資料庫的 id 都是1開始自增上去的,如發起為id值為 -1 的資料或 id 為特别大不存在的資料。這時的使用者很可能是攻擊者,攻擊會導緻資料庫壓力過大,嚴重會擊垮資料庫。Redis還有一個進階用法布隆過濾器(Bloom Filter)這個也能很好的防止緩存穿透的發生,他的原理也很簡單就是利用高效的資料結構和算法快速判斷出你這個Key是否在資料庫中存在,不存在你return就好了,存在你就去查了DB重新整理KV再return。

緩存擊穿:這個跟緩存雪崩有點像,但是又有一點不一樣,緩存雪崩是因為大面積的緩存失效,打崩了DB,而緩存擊穿不同的是緩存擊穿是指一個Key非常熱點,在不停的扛着大并發,大并發集中對這一個點進行通路,當這個Key在失效的瞬間,持續的大并發就穿破緩存,直接請求資料庫,就像在一個完好無損的桶上鑿開了一個洞。

緩存穿透和緩存擊穿分别怎麼解決?

緩存穿透:我會在接口層增加校驗,比如使用者鑒權校驗,參數做校驗,不合法的參數直接代碼Return,比如:id 做基礎校驗,id <=0的直接攔截等。我們在開發程式的時候都要有一顆“不信任”的心,就是不要相信任何調用方,如果上遊或者下遊挂掉了怎麼辦,參數不合理怎麼處理等。

緩存擊穿:從緩存取不到的資料,在資料庫中也沒有取到,這時也可以将對應Key的Value對寫為null、位置錯誤、稍後重試這樣的值具體取啥問産品,或者看具體的場景,緩存有效時間可以設定短點,如30秒(設定太長會導緻正常情況也沒法使用)。同時在擷取資料庫裡的資料的時候,也需要進行加鎖控制,保證同一時間隻有一個線程去請求資料庫,重新整理緩存。還可以通過自旋的方式,或者信号量的方式

5、redis-哨兵、持久化、主從

見上 redis-主從複制

6、LRU

7、redis 實作異步隊列

我們在實際開發中經常會有專業的消息隊列中間件,但是如果系統中沒有mq中間件,又懶得維護mq中間件,那麼我們可以通過redis來實作 因為redis并不是專業實作隊列的中間件,是以在實作方式上還是會存在一些問題,還是比不上rocketMq之類的中間件。redis實作隊列主要是使用資料結構中的list,因為它是按照塞入順序排序的結構,我們就可以按照左邊塞入,右邊取出的方式來實作先入先出的隊列需求 彈出時使用blpop的方法而不是lpop方法是因為如果使用lpop方法的話會造成如果隊列中沒有資料,連接配接一直空置的情況,是以使用blpop的方法可以在沒有資料的時候将連接配接阻塞,在有資料時再讀取 。 當然使用blpop同樣可能存在長時間沒有資料,redis将連接配接斷掉的情況,是以就需要我們在使用時将這種情況的異常也考慮進去,在catch中将連接配接重建立立之類。

7、緩存一緻性問題怎麼解決?

7.1 先删緩存,再更新資料庫

存在的問題

先删除緩存,資料庫還沒有更新成功,此時如果讀取緩存,緩存不存在,去資料庫中讀取到的是舊值,緩存不一緻發生。

解決方案

延時雙删的方案的思路是,為了避免更新資料庫的時候,其他線程從緩存中讀取不到資料,就在更新完資料庫之後,再sleep一段時間,然後再次删除緩存。sleep的時間要對業務讀寫緩存的時間做出評估,sleep時間大于讀寫緩存的時間即可。

流程如下:

1、線程1删除緩存,然後去更新資料庫

2、線程2來讀緩存,發現緩存已經被删除,是以直接從資料庫中讀取,這時候由于線程1還沒有更新完成,是以讀到的是舊值,然後把舊值寫入緩存

3、線程1,根據估算的時間,sleep,由于sleep的時間大于線程2讀資料+寫緩存的時間,是以緩存被再次删除

4、如果還有其他線程來讀取緩存的話,就會再次從資料庫中讀取到最新值

7.2 先更新資料庫,再删除緩存

存在的問題

反過來操作,先更新資料庫,再删除緩存呢?這個就更明顯的問題了,更新資料庫成功,如果删除緩存失敗或者還沒有來得及删除,那麼,其他線程從緩存中讀取到的就是舊值,還是會發生不一緻。

解決方案

消息隊列先更新資料庫,成功後往消息隊列發消息,消費到消息後再删除緩存,借助消息隊列的重試機制來實作,達到最終一緻性的效果。這個解決方案其實問題更多。引入消息中間件之後,問題更複雜了,怎麼保證消息不丢失更麻煩就算更新資料庫和删除緩存都沒有發生問題,消息的延遲也會帶來短暫的不一緻性,不過這個延遲相對來說還是可以接受的

為了解決緩存一緻性的問題單獨引入一個消息隊列,太複雜了。一般大公司本身都會有監聽binlog消息的消息隊列存在,主要是為了做一些核對的工作。這樣,我們可以借助監聽binlog的消息隊列來做删除緩存的操作。這樣做的好處是,不用你自己引入,侵入到你的業務代碼中,中間件幫你做了解耦,同時,中間件的這個東西本身就保證了高可用。當然,這樣消息延遲的問題依然存在,但是相比單純引入消息隊列的做法更好一點。如果并發不是特别高的話,這種做法的實時性和一緻性都還算可以接受的。

設定緩存過期時間

每次放入緩存的時候,設定一個過期時間,比如5分鐘,以後的操作隻修改資料庫,不操作緩存,等待緩存逾時後從資料庫重新讀取。如果對于一緻性要求不是很高的情況,可以采用這種方案。這個方案還會有另外一個問題,就是如果資料更新的特别頻繁,不一緻性的問題就很大了。

8、redis 實作分布式鎖

繼續閱讀