Redis面試題集錦(精選)
阿新 • • 發佈:2020-03-11
![Redis面試題集錦(精選)](https://upload-images.jianshu.io/upload_images/7326374-3acce3a2228e77a0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
### 1.什麼是 Redis?簡述它的優缺點?
**Redis**的全稱是:`Remote Dictionary.Server`,本質上是一個`Key-Value` 型別的記憶體資料庫,很像`memcached`,整個資料庫統統載入在記憶體當中進行操作,定期通過非同步操作把資料庫資料`flush `到硬碟上進行儲存。因為是純記憶體操作,`Redis` 的效能非常出色,每秒可以處理超過 **10** 萬次讀寫操作,是已知效能最快的**Key-Value DB**。
**Redis** 的出色之處不僅僅是效能,`Redis` 最大的魅力是支援儲存多種資料結構,此外單個 `value` 的最大限制是 `1GB`,不像 memcached 只能儲存 1MB 的資料,因此 Redis 可以用來實現很多有用的功能。
比方說用他的 `List` 來做 `FIFO` 雙向連結串列,實現一個輕量級的高性 能訊息佇列服務,用他的 `Set`可以做高效能的 `tag `系統等等。
另外 `Redis` 也可以對存入的 `Key-Value` 設定 `expire` 時間,因此也可以被當作一個功能加強版的memcached 來用。 Redis 的主要缺點是資料庫容量受到實體記憶體的限制,不能用作海量資料的高效能讀寫,因此 Redis 適合的場景主要侷限在較小資料量的高效能操作和運算上。
### 2.Redis 支援的 Java 客戶端都有哪些?官方推薦用哪個?
Redisson、Jedis、lettuce 等等,官方推薦使用 `Redisson`。
### 3.Redis 與 Memcached 相比有哪些優勢?
- `memcached` 所有的值均是簡單的字串,`redis` 作為其替代者,支援更為豐富的資料型別。
- `redis` 的速度比 `memcached` 快很多。
- `redis` 可以持久化其資料。
![Redis 與 Memcached區別](https://upload-images.jianshu.io/upload_images/7326374-2c0a68b348a67eb0.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
### 4.Redis 支援哪幾種資料型別?並簡單介紹一下?
String(字串)、Hash(雜湊)、List(列表)、Set(集合)、Zset(sorted set:有序集合)
**String(字串)**
String是redis最基本的型別,你可以理解成與Memcached一模一樣的型別,一個key對應一個value。
String型別是二進位制安全的。意思是redis的string可以包含任何資料。比如jpg圖片或者序列化的物件 。
String型別是Redis最基本的資料型別,一個鍵最大能儲存512MB。
> 一個鍵能存512M,但是取出來的時候就要注意了。如果存的value過大,卻用String接收的話,就會拋異常了。我們就出現過線上問題~
**Hash(雜湊)**
Redis hash是一個鍵值對集合。
Redis hash是一個string型別的field和value的對映表,hash特別適合用於儲存物件。每個 hash 可以儲存 2^32 - 1鍵值對(40多億)。
**List(列表)**
Redis 列表是簡單的字串列表,按照插入順序排序。你可以新增一個元素導列表的頭部(左邊)或者尾部(右邊)。
列表最多可儲存 2^32 - 1元素 (4294967295, 每個列表可儲存40多億)。
**Set(集合)**
Redis的Set是string型別的無序集合。
集合是通過雜湊表實現的,所以新增,刪除,查詢的複雜度都是O(1)。
sadd 命令:新增一個string元素到,key對應的set集合中,成功返回1,如果元素以及在集合中返回0,key對應的set不存在返回錯誤。
**Zset**
`Redis` `zset` 和`set `一樣也是`string`型別元素的集合,且不允許重複的成員。不同的是每個元素都會關聯一個`double`型別的分數。`redis`正是通過分數來為集合中的成員進行從小到大的排序。
`zset`的成員是唯一的,但分數(`score`)卻可以重複。
`zadd`命令:新增元素到集合,元素在集合中存在則更新對應`score`.
### 5.怎麼理解 Redis 事務?相關事務命令有哪些?並介紹一下
事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行,事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷。事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。
命令:`MULTI`、`EXEC`、`DISCARD`、`WATCH`、`UNWATCH`.
- DISCARD
取消事務,放棄執行事務塊內的所有命令。
- EXEC
執行所有事務塊內的命令。
- MULTI
標記一個事務塊的開始。
- UNWATCH
取消 WATCH 命令對所有 key 的監視。
- WATCH key [key ...]
監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動,那麼事務將被打斷。
在傳統的關係式資料庫中,常常用 `ACID` 性質來檢驗事務功能的可靠性和安全性。在 `Redis `中,事務總是具有**原子性**(Atomicity)、**一致性**(Consistency)和**隔離性**(Isolation),並且當 Redis 執行在某種特定的持久化模式下時,事務也具有**永續性**(Durability)。
### 6.為什麼要用 redis 而不用 map/guava 做快取?
快取分為本地快取和分散式快取。以 `Java` 為例,使用自帶的` map `或者 `guava `實現的是本地快取,最主要的特點是輕量以及快速,生命週期隨著 `jvm` 的銷燬而結束,並且在多例項的情況下,每個例項都需要各自儲存一份快取,快取不具有一致性。
使用 `redis` 或` memcached `之類的稱為分散式快取,在多例項的情況下,各例項共用一份快取資料,快取具有一致性。缺點是需要保持 `redis` 或 `memcached`服務的高可用,整個程式架構上較為複雜。
### 7. redis 設定過期時間
Redis中有個設定時間過期的功能,即對儲存在 redis 資料庫中的值可以設定一個過期時間。作為一個快取資料庫,這是非常實用的。如我們一般專案中的 token 或者一些登入資訊,尤其是簡訊驗證碼都是有時間限制的,按照傳統的資料庫處理方式,一般都是自己判斷過期,這樣無疑會嚴重影響專案效能。
我們 set key 的時候,都可以給一個 expire time,就是過期時間,通過過期時間我們可以指定這個 key 可以存活的時間。
如果假設你設定了一批 key 只能存活1個小時,那麼接下來1小時後,redis是怎麼對這批key進行刪除的?
**定期刪除**+**惰性刪除**。
通過名字大概就能猜出這兩個刪除方式的意思了。
定期刪除:redis預設是每隔 100ms 就隨機抽取一些設定了過期時間的key,檢查其是否過期,如果過期就刪除。注意這裡是隨機抽取的。為什麼要隨機呢?你想一想假如 redis 存了幾十萬個 key ,每隔100ms就遍歷所有的設定過期時間的 key 的話,就會給 CPU 帶來很大的負載!
惰性刪除 :定期刪除可能會導致很多過期 key 到了時間並沒有被刪除掉。所以就有了惰性刪除。假如你的過期 key,靠定期刪除沒有被刪除掉,還停留在記憶體裡,除非你的系統去查一下那個 key,才會被redis給刪除掉。這就是所謂的惰性刪除,也是夠懶的哈!
但是僅僅通過設定過期時間還是有問題的。我們想一下:如果定期刪除漏掉了很多過期 key,然後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?如果大量過期key堆積在記憶體裡,導致redis記憶體塊耗盡了。怎麼解決這個問題呢?
### 8. 說一說Redis 記憶體淘汰機制(MySQL裡有2000w資料,Redis中只存20w的資料,如何保證Redis中的資料都是熱點資料?)
`redis` 配置檔案 `redis.conf` 中有相關注釋,大家可以自行查閱或者通過這個網址檢視:
http://download.redis.io/redis-stable/redis.conf
`redis` 提供 6種資料淘汰策略:
**volatile-lru**:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰
**volatile-ttl**:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰
**volatile-random**:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰
**allkeys-lru**:當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近最少使用的key(這個是最常用的).
**allkeys-random**:從資料集(server.db[i].dict)中任意選擇資料淘汰
**no-eviction**:禁止驅逐資料,也就是說當記憶體不足以容納新寫入資料時,新寫入操作會報錯。這個應該沒人使用吧!
### 9. 如何解決 Redis 的併發競爭 Key 問題?
所謂 `Redis` 的併發競爭 `Key` 的問題也就是多個系統同時對一個 `key` 進行操作,但是最後執行的順序和我們期望的順序不同,這樣也就導致了結果的不同!
推薦一種方案:**分散式鎖**(`zookeeper` 和 `redis` 都可以實現分散式鎖)。(如果不存在 `Redis` 的併發競爭 `Key` 問題,不要使用分散式鎖,這樣會影響效能)
基於`zookeeper`臨時有序節點可以實現的分散式鎖。
大致思想為:每個客戶端對某個方法加鎖時,在`zookeeper`上的與該方法對應的指定節點的目錄下,生成一個唯一的瞬時有序節點。 判斷是否獲取鎖的方式很簡單,只需要判斷有序節點中序號最小的一個。 當釋放鎖的時候,只需將這個瞬時節點刪除即可。同時,其可以避免服務宕機導致的鎖無法釋放,而產生的死鎖問題。完成業務流程後,刪除對應的子節點釋放鎖。
在實踐中,當然是從以可靠性為主。所以首推`Zookeeper`。
### 10.Redis 為什麼是單執行緒的?
官方FAQ表示,因為Redis是基於記憶體的操作,CPU不是Redis的瓶頸,Redis的瓶頸最有可能是機器記憶體的大小或者網路頻寬。既然單執行緒容易實現,而且CPU不會成為瓶頸,那就順理成章地採用單執行緒的方案了(畢竟採用多執行緒會有很多麻煩!)Redis利用佇列技術將併發訪問變為序列訪問
1)絕大部分請求是純粹的記憶體操作(非常快速)
2)採用單執行緒,避免了不必要的上下文切換和競爭條件
3)非阻塞IO優點:
1.速度快,因為資料存在記憶體中,類似於HashMap,HashMap的優勢就是查詢和操作的時間複雜度都是O(1)
2. 支援豐富資料型別,支援string,list,set,sorted set,hash
3.支援事務,操作都是原子性,所謂的原子性就是對資料的更改要麼全部執行,要麼全部不執行
4. 豐富的特性:可用於快取,訊息,按key設定過期時間,過期後將會自動刪除如何解決redis的併發競爭key問題
同時有多個子系統去set一個key。這個時候要注意什麼呢? 不推薦使用redis的事務機制。因為我們的生產環境,基本都是redis叢集環境,做了資料分片操作。你一個事務中有涉及到多個key操作的時候,這多個key不一定都儲存在同一個redis-server上。因此,redis的事務機制,十分雞肋。
(1)如果對這個key操作,不要求順序: 準備一個分散式鎖,大家去搶鎖,搶到鎖就做set操作即可
(2)如果對這個key操作,要求順序: 分散式鎖+時間戳。 假設這會系統B先搶到鎖,將key1設定為{valueB 3:05}。接下來系統A搶到鎖,發現自己的valueA的時間戳早於快取中的時間戳,那就不做set操作了。以此類推。
(3) 利用佇列,將set方法變成序列訪問也可以redis遇到高併發,如果保證讀寫key的一致性
對redis的操作都是具有原子性的,是執行緒安全的操作,你不用考慮併發問題,redis內部已經幫你處理好併發的問題了。
### 11.說一說Redis 持久化機制?
`Redis`是一個支援持久化的記憶體資料庫,通過持久化機制把記憶體中的資料同步到硬碟檔案來保證資料持久化。當`Redis`重啟後通過把硬碟檔案重新載入到記憶體,就能達到恢復資料的目的。
實現:單獨建立`fork()`一個子程序,將當前父程序的資料庫資料複製到子程序的記憶體中,然後由子程序寫入到臨時檔案中,持久化的過程結束了,再用這個臨時檔案替換上次的快照檔案,然後子程序退出,記憶體釋放。
- **RDB**是`Redis`預設的持久化方式。按照一定的時間週期策略把記憶體的資料以快照的形式儲存到硬碟的二進位制檔案。即Snapshot快照儲存,對應產生的資料檔案為dump.rdb,通過配置檔案中的save引數來定義快照的週期。( 快照可以是其所表示的資料的一個副本,也可以是資料的一個複製品。)
- **AOF**:`Redis`會將每一個收到的寫命令都通過`Write`函式追加到檔案最後,類似於`MySQL`的`binlog`。當`Redis`重啟是會通過重新執行檔案中儲存的寫命令來在記憶體中重建整個資料庫的內容。
當兩種方式同時開啟時,資料恢復`Redis`會優先選擇`AOF`恢復。
### 12.熱點資料和冷資料是什麼?
**熱點資料**,快取才有價值
對於熱點資料,比如我們的某IM產品,生日祝福模組,當天的壽星列表,快取以後可能讀取數十萬次。再舉個例子,某導航產品,我們將導航資訊,快取以後可能讀取數百萬次。
對於冷資料而言,大部分資料可能還沒有再次訪問到就已經被擠出記憶體,不僅佔用記憶體,而且價值不大。頻繁修改的資料,看情況考慮使用快取
對於上面兩個例子,壽星列表、導航資訊都存在一個特點,就是資訊修改頻率不高,讀取通常非常高的場景。
**資料更新前至少讀取兩次**,快取才有意義。這個是最基本的策略,如果快取還沒有起作用就失效了,那就沒有太大價值了。
那存不存在,修改頻率很高,但是又不得不考慮快取的場景呢?有!比如,這個讀取介面對資料庫的壓力很大,但是又是熱點資料,這個時候就需要考慮通過快取手段,減少資料庫的壓力,比如我們的某助手產品的,點贊數,收藏數,分享數等是非常典型的熱點資料,但是又不斷變化,此時就需要將資料同步儲存到Redis快取,減少資料庫壓力。
### 13. 簡單說一說快取雪崩以及解決辦法?
**快取雪崩**我們可以簡單的理解為:**由於原有快取失效,新快取未到期間**
(例如:我們設定快取時採用了相同的過期時間,在同一時刻出現大面積的快取過期),所有原本應該訪問快取的請求都去查詢資料庫了,而對資料庫CPU和記憶體造成巨大壓力,嚴重的會造成資料庫宕機。從而形成一系列連鎖反應,造成整個系統崩潰。
**解決辦法**:
大多數系統設計者考慮用加鎖( 最多的解決方案)或者佇列的方式保證來保證不會有大量的執行緒對資料庫一次性進行讀寫,從而避免失效時大量的併發請求落到底層儲存系統上。還有一個簡單方案就時講快取失效時間分散開。
### 14. 簡單說一說快取穿透以及解決辦法?
**快取穿透**是指使用者查詢資料,在資料庫沒有,自然在快取中也不會有。這樣就導致使用者查詢的時候,在快取中找不到,每次都要去資料庫再查詢一遍,然後返回空(相當於進行了兩次無用的查詢)。這樣請求就繞過快取直接查資料庫,這也是經常提的快取命中率問題。
**解決辦法**:
最常見的則是採用布隆過濾器,將所有可能存在的資料雜湊到一個足夠大的bitmap中,一個一定不存在的資料會被這個bitmap攔截掉,從而避免了對底層儲存系統的查詢壓力。
另外也有一個更為簡單粗暴的方法,如果一個查詢返回的資料為空(不管是資料不存在,還是系統故障),我們仍然把這個空結果進行快取,但它的過期時間會很短,最長不超過五分鐘。通過這個直接設定的預設值存放到快取,這樣第二次到緩衝中獲取就有值了,而不會繼續訪問資料庫,這種辦法最簡單粗暴。
5TB的硬碟上放滿了資料,請寫一個演算法將這些資料進行排重。如果這些資料是一些32bit大小的資料該如何解決?如果是64bit的呢?
對於空間的利用到達了一種極致,那就是Bitmap和布隆過濾器(Bloom Filter)。
**Bitmap**: 典型的就是雜湊表
缺點是,Bitmap對於每個元素只能記錄1bit資訊,如果還想完成額外的功能,恐怕只能靠犧牲更多的空間、時間來完成了。
**布隆過濾器**(推薦)
就是引入了k(k>1)k(k>1)個相互獨立的雜湊函式,保證在給定的空間、誤判率下,完成元素判重的過程。
它的優點是空間效率和查詢時間都遠遠超過一般的演算法,缺點是有一定的誤識別率和刪除困難。
Bloom-Filter演算法的核心思想就是利用多個不同的Hash函式來解決“衝突”。
Hash存在一個衝突(碰撞)的問題,用同一個Hash得到的兩個URL的值有可能相同。為了減少衝突,我們可以多引入幾個Hash,如果通過其中的一個Hash值我們得出某元素不在集合中,那麼該元素肯定不在集合中。只有在所有的Hash函式告訴我們該元素在集合中時,才能確定該元素存在於集合中。這便是Bloom-Filter的基本思想。
Bloom-Filter一般用於在大資料量的集合中判定某元素是否存在。
### 15. 簡單說一說快取預熱?
除了快取伺服器自帶的快取失效策略之外(Redis預設的有6中策略可供選擇),我們還可以根據具體的業務需求進行自定義的快取淘汰,常見的策略有兩種:
(1)定時去清理過期的快取;
(2)當有使用者請求過來時,再判斷這個請求所用到的快取是否過期,過期的話就去底層系統得到新資料並更新快取。
兩者各有優劣,第一種的缺點是維護大量快取的key是比較麻煩的,第二種的缺點就是每次使用者請求過來都要判斷快取失效,邏輯相對比較複雜!具體用哪種方案,大家可以根據自己的應用場景來權衡。
### 16. 簡單說一說快取降級?
當訪問量劇增、服務出現問題(如響應時間慢或不響應)或非核心服務影響到核心流程的效能時,仍然需要保證服務還是可用的,即使是有損服務。系統可以根據一些關鍵資料進行自動降級,也可以配置開關實現人工降級。
降級的最終目的是保證核心服務可用,即使是有損的。而且有些服務是無法降級的(如加入購物車、結算)。
以參考日誌級別設定預案:
(1)一般:比如有些服務偶爾因為網路抖動或者服務正在上線而超時,可以自動降級;
(2)警告:有些服務在一段時間內成功率有波動(如在95~100%之間),可以自動降級或人工降級,併發送告警;
(3)錯誤:比如可用率低於90%,或者資料庫連線池被打爆了,或者訪問量突然猛增到系統能承受的最大閥值,此時可以根據情況自動降級或者人工降級;
(4)嚴重錯誤:比如因為特殊原因資料錯誤了,此時需要緊急人工降級。
服務降級的目的,是為了防止Redis服務故障,導致資料庫跟著一起發生雪崩問題。因此,對於不重要的快取資料,可以採取服務降級策略,例如一個比較常見的做法就是,Redis出現問題,不去資料庫查詢,而是直接返回預設值給使用者。
### 17.Redis 常見效能問題和解決方案?
(1) Master 最好不要做任何持久化工作,如 RDB 記憶體快照和 AOF 日誌檔案
(2) 如果資料比較重要,某個 Slave 開啟 AOF 備份資料,策略設定為每秒同步一次
(3) 為了主從複製的速度和連線的穩定性, Master 和 Slave 最好在同一個區域網內
(4) 儘量避免在壓力很大的主庫上增加從庫
(5) 主從複製不要用圖狀結構,用單向連結串列結構更為穩定,即: Master <- Slave1 <- Slave2 <- Slave3…
### 18.Redis如何實現分散式鎖?
1.根據lockKey區進行setnx(set not exist,如果key值為空,則正常設定,返回1,否則不會進行設定並返回0)操作,如果設定成功,表示已經獲得鎖,否則並沒有獲取鎖。
2.如果沒有獲得鎖,去Redis上拿到該key對應的值,在該key上我們儲存一個時間戳(用毫秒錶示,t1),為了避免死鎖以及其他客戶端佔用該鎖超過一定時間(5秒),使用該客戶端當前時間戳,與儲存的時間戳作比較。
3.如果沒有超過該key的使用時限,返回false,表示其他人正在佔用該key,不能強制使用;如果已經超過時限,那我們就可以進行解鎖,使用我們的時間戳來代替該欄位的值。
4.但是如果在setnx失敗後,get該值卻無法拿到該欄位時,說明操作之前該鎖已經被釋放,這個時候,最好的辦法就是重新執行一遍setnx方法來獲取其值以獲得該鎖。
![Redis分散式鎖流程圖](https://upload-images.jianshu.io/upload_images/7326374-1219d618bce23643.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
詳細內容可以檢視:Redis與Zookeeper實現分散式鎖的區別(https://www.cnblogs.com/mengchunchen/p/9647756.html)
### 19.如何保證快取與資料庫雙寫時的資料一致性?
你只要用快取,就可能會涉及到快取與資料庫雙儲存雙寫,你只要是雙寫,就一定會有資料一致性的問題,那麼你如何解決一致性問題?
一般來說,就是如果你的系統不是嚴格要求快取+資料庫必須一致性的話,快取可以稍微的跟資料庫偶爾有不一致的情況,最好不要做這個方案,讀請求和寫請求序列化,串到一個記憶體佇列裡去,這樣就可以保證一定不會出現不一致的情況
序列化之後,就會導致系統的吞吐量會大幅度的降低,用比正常情況下多幾倍的機器去支撐線上的一個請求。
還有一種方式就是可能會暫時產生不一致的情況,但是發生的機率特別小,就是先更新資料庫,然後再刪除快取。
這種情況不存在併發問題麼?
不是的。假設這會有兩個請求,一個請求A做查詢操作,一個請求B做更新操作,那麼會有如下情形產生
(1)快取剛好失效
(2)請求A查詢資料庫,得一箇舊值
(3)請求B將新值寫入資料庫
(4)請求B刪除快取
(5)請求A將查到的舊值寫入快取
ok,如果發生上述情況,確實是會發生髒資料。
然而,發生這種情況的概率又有多少呢?
發生上述情況有一個先天性條件,就是步驟(3)的寫資料庫操作比步驟(2)的讀資料庫操作耗時更短,才有可能使得步驟(4)先於步驟(5)。可是,大家想想,資料庫的讀操作的速度遠快於寫操作的(不然做讀寫分離幹嘛,做讀寫分離的意義就是因為讀操作比較快,耗資源少),因此步驟(3)耗時比步驟(2)更短,這一情形很難出現。
如何解決上述併發問題?
首先,給快取設有效時間是一種方案。其次,採用非同步延時刪除策略,保證讀請求完成以後,再進行刪除操作。
### 20.是否使用過Redis叢集,叢集的原理是什麼?
`Redis Sentinal`著眼於高可用,在`master`宕機時會自動將`slave`提升為`master`,繼續提供服務。
`Redis Cluster`著眼於擴充套件性,在單個`redis`記憶體不足時,使用`Cluster`進行分片儲存。
**Slot**:插槽,可以儲存兩個數值的一個變數這個變數的取值範圍是:0-16383。
**Cluster**:叢集管理者,使叢集對外暴漏的是一個整體。
**redis cluster**:採用虛擬分割槽的方式,將整個叢集看成一個整體,然後分成16384個槽位。
然後再將16484個槽位分別分配給叢集的各個節點,然後各個節點各自負責一部分槽位。
![Redis叢集](https://upload-images.jianshu.io/upload_images/7326374-f534036d48705138.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**原理:**
節點1負責 `0-5000`之間的槽位,節點2負責`5001-10000`之間的槽位,節點3負責`10001-16383`之間的槽位。
k-v鍵值對資料只會和槽位相關,與物理機器無關。通過crc16演算法計算出 k對應的整數值(有點類似hash),然後對算出的整數值%16384取模,計算出k-v對應在哪個槽位上,然後再根據槽位與機器節點的對映關係,儲存到相應的節點上去。取的時候,也是相應的過 程所以整個叢集協同一致對外,給client看到的檢視就是完整的資料集。
### 21. redis事物的瞭解CAS(check-and-set 操作實現樂觀鎖 )?
和眾多其它資料庫一樣,Redis作為NoSQL資料庫也同樣提供了事務機制。在Redis中,MULTI/EXEC/DISCARD/WATCH這四個命令是我們實現事務的基石。相信對有關係型資料庫開發經驗的開發者而言這一概念並不陌生,即便如此,我們還是會簡要的列出Redis中事務的實現特徵:
1). 在事務中的所有命令都將會被序列化的順序執行,事務執行期間,Redis不會再為其它客戶端的請求提供任何服務,從而保證了事物中的所有命令被原子的執行。
2). 和關係型資料庫中的事務相比,在Redis事務中如果有某一條命令執行失敗,其後的命令仍然會被繼續執行。
3). 我們可以通過MULTI命令開啟一個事務,有關係型資料庫開發經驗的人可以將其理解為"BEGIN TRANSACTION"語句。在該語句之後執行的命令都將被視為事務之內的操作,最後我們可以通過執行EXEC/DISCARD命令來提交/回滾該事務內的所有操作。這兩個Redis命令可被視為等同於關係型資料庫中的COMMIT/ROLLBACK語句。
4). 在事務開啟之前,如果客戶端與伺服器之間出現通訊故障並導致網路斷開,其後所有待執行的語句都將不會被伺服器執行。然而如果網路中斷事件是發生在客戶端執行EXEC命令之後,那麼該事務中的所有命令都會被伺服器執行。
5). 當使用Append-Only模式時,Redis會通過呼叫系統函式write將該事務內的所有寫操作在本次呼叫中全部寫入磁碟。然而如果在寫入的過程中出現系統崩潰,如電源故障導致的宕機,那麼此時也許只有部分資料被寫入到磁碟,而另外一部分資料卻已經丟失。
Redis伺服器會在重新啟動時執行一系列必要的一致性檢測,一旦發現類似問題,就會立即退出並給出相應的錯誤提示。此時,我們就要充分利用Redis工具包中提供的redis-check-aof工具,該工具可以幫助我們定位到資料不一致的錯誤,並將已經寫入的部分資料進行回滾。修復之後我們就可以再次重新啟動Redis伺服器了。
### 22.WATCH命令和基於CAS的樂觀鎖?
在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。假設我們通過WATCH命令在事務執行之前監控了多個Keys,倘若在WATCH之後有任何Key的值發生了變化,EXEC命令執行的事務都將被放棄,同時返回Null multi-bulk應答以通知呼叫者事務
執行失敗。例如,我們再次假設Redis中並未提供incr命令來完成鍵值的原子性遞增,如果要實現該功能,我們只能自行編寫相應的程式碼。其偽碼如下:
val = GET mykey
val = val + 1
SET mykey $val
以上程式碼只有在單連線的情況下才可以保證執行結果是正確的,因為如果在同一時刻有多個客戶端在同時執行該段程式碼,那麼就會出現多執行緒程式中經常出現的一種錯誤場景--競態爭用(race condition)。比如,客戶端A和B都在同一時刻讀取了mykey的原有值,假設該值為10,此後兩個客戶端又均將該值加一後set回Redis伺服器,這樣就會導致mykey的結果為11,而不是我們認為的12。為了解決類似的問題,我們需要藉助WATCH命令的幫助,見如下程式碼:
```
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
```
和此前程式碼不同的是,新程式碼在獲取mykey的值之前先通過WATCH命令監控了該鍵,此後又將set命令包圍在事務中,這樣就可以有效的保證每個連線在執行EXEC之前,如果當前連接獲取的mykey的值被其它連線的客戶端修改,那麼當前連線的EXEC命令將執行失敗。這樣呼叫者在判斷返回值後就可以獲悉val是否被重新設定成功。
### 23.redis 最適合的場景有哪些?
Redis最適合所有資料`in-momory`的場景,雖然Redis也提供持久化功能,但實際更多的是一個`disk-backed`的功能,跟傳統意義上的持久化有比較大的差別,那麼可能大家就會有疑問,似乎Redis更像一個加強版的`Memcached`,那麼何時使用`Memcached`,何時使用Redis呢?
如果簡單地比較Redis與Memcached的區別,大多數都會得到以下觀點:
- Redis不僅僅支援簡單的k/v型別的資料,同時還提供`list`,`set`,`zset`,`hash`等資料結構的儲存。
- Redis支援資料的備份,即`master-slave`模式的資料備份。
- Redis支援資料的持久化,可以將記憶體中的資料保持在磁碟中,重啟的時候可以再次載入進行使用。
**1.會話快取(Session Cache)**
最常用的一種使用Redis的情景是會話快取(session cache)。用Redis快取會話比其他儲存(如Memcached)的優勢在於:Redis提供持久化。當維護一個不是嚴格要求一致性的快取時,如果使用者的購物車資訊全部丟失,大部分人都會不高興的,現在,他們還會這樣嗎?
幸運的是,隨著 Redis 這些年的改進,很容易找到怎麼恰當的使用Redis來快取會話的文件。甚至廣為人知的商業平臺Magento也提供Redis的外掛。
**2.全頁快取(FPC)**
除基本的會話token之外,Redis還提供很簡便的`FPC`平臺。回到一致性問題,即使重啟了Redis例項,因為有磁碟的持久化,使用者也不會看到頁面載入速度的下降,這是一個極大改進,類似PHP本地FPC。
再次以Magento為例,Magento提供一個外掛來使用Redis作為全頁快取後端。
此外,對WordPress的使用者來說,Pantheon有一個非常好的外掛 wp-redis,這個外掛能幫助你以最快速度載入你曾瀏覽過的頁面。
**3. 佇列**
Reids在記憶體儲存引擎領域的一大優點是提供 list 和 set 操作,這使得Redis能作為一個很好的訊息佇列平臺來使用。Redis作為佇列使用的操作,就類似於本地程式語言(如Python)對 list 的 push/pop 操作。
如果你快速的在Google中搜索“Redis queues”,你馬上就能找到大量的開源專案,這些專案的目的就是利用Redis建立非常好的後端工具,以滿足各種佇列需求。例如,Celery有一個後臺就是使用Redis作為broker,你可以從這裡去檢視。
**4.排行榜/計數器**
Redis在記憶體中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(Sorted Set)也使得我們在執行這些操作的時候變的非常簡單,Redis只是正好提供了這兩種資料結構。
所以,我們要從排序集合中獲取到排名最靠前的10個使用者–我們稱之為“user_scores”,我們只需要像下面一樣執行即可:
當然,這是假定你是根據你使用者的分數做遞增的排序。如果你想返回使用者及使用者的分數,你需要這樣執行:
```
ZRANGE user_scores 0 10 WITHSCORES
```
Agora Games就是一個很好的例子,用Ruby實現的,它的排行榜就是使用Redis來儲存資料的,你可以在這裡看到。
**5.釋出/訂閱**
最後(但肯定不是最不重要的)是Redis的釋出/訂閱功能。釋出/訂閱的使用場景確實非常多。我已看見人們在社交網路連線中使用,還可作為基於釋出/訂閱的指令碼觸發器,甚至用Redis的釋出/訂閱功能來建立聊天系統!(不,這是真的,你可以去核實)。
Redis提供的所有特性中,我感覺這個是喜歡的人最少的一個,雖然它為使用者提供如果此多功能。
### 24. 說說Redis雜湊槽的概念?
Redis叢集沒有使用一致性hash,而是引入了雜湊槽的概念,Redis叢集有**16384**個雜湊槽,每個key通過**CRC16**校驗後對**16384**取模來決定放置哪個槽,叢集的每個節點負責一部分hash槽。
### 25. Redis叢集方案什麼情況下會導致整個叢集不可用?
有A,B,C三個節點的叢集,在沒有複製模型的情況下,如果節點B失敗了,那麼整個叢集就會以為缺少`5501-11000`這個範圍的槽而不可用。
關於Redis叢集架構更加詳細的內容,可以看看大佬的文章:
那些年用過的Redis叢集架構(含面試解析):(https://www.cnblogs.com/rjzheng/p/10360619.html)
## 推薦
[Spring面試題集錦(精選)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484099&idx=1&sn=8e7ad8e24c2ced9bc9a5bee16ffea66a&chksm=96e673d0a191fac60672313b7f8031d6b76daa1d5eec4b7c698505877fc2cc3a21c47cf2a922&token=1975823476&lang=zh_CN#rd)
[SpringMVC面試題集錦(精選)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484113&idx=1&sn=e6c5c5942152ee1cd6bacbab571a766c&chksm=96e673c2a191fad42c54a718dbfcfad8f4a32448f63c832e081eea2a130968f5a12bb165c6bb&token=1513909591&lang=zh_CN#rd)
[Spring全家桶註解一覽(精選)](https://mp.weixin.qq.com/s?__biz=MzIwMTg3NzYyOA==&mid=2247484108&idx=1&sn=ea9de1f2e9e8640002a1ddb85e3c78a8&chksm=96e673dfa191fac9bd60c66dcccc4bb0d4fdec3d9e3db5f4237b0bd638d843a0de18d7095594&token=1975823476&lang=zh_CN#rd)
[ProcessOn是一個線上作圖工具的聚合平臺~](https://www.processon.com/i/5cd53c2fe4b01941c8cf1c21)
## 文末
> 歡迎關注個人微信公眾號:**Coder程式設計**
歡迎關注**Coder程式設計**公眾號,主要分享資料結構與演算法、Java相關知識體系、框架知識及原理、Spring全家桶、微服務專案實戰、DevOps實踐之路、每日一篇網際網路大廠面試或筆試題以及PMP專案管理知識等。更多精彩內容正在路上~
>文章收錄至
Github: https://github.com/CoderMerlin/coder-programming
Gitee: https://gitee.com/573059382/coder-programming
歡迎**關注**並star~
![微信公眾號](https://upload-images.jianshu.io/upload_images/7326374-0c30c361239e4cca?imageMogr2/auto-orient/strip%7CimageView2/2/