前言
本文主要是針對Redis的高頻知識點整理出來的面試題,答案大部分參考網上,僅供復習參考。本文中如果出現解答錯誤希望指出,共同進步,共同學習。

正文
Q1:你了解Redis,能介紹一下Redis嗎?
Redis(全稱:Remote Dictionary Server 遠程字典服務)是一個開源的使用ANSIC語言編寫、支持網絡、可基於內存亦可持久化的日志型、Key-Value數據庫,遵守BSD協議。是一個非關系型數據庫(NoSQL)。
Q2:Redis與其他數據庫有什么很大的優勢嗎?
- 性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
- 豐富的數據類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操作。
- 事務 – Redis的所有操作都是原子性的,意思就是要么成功執行要么失敗完全不執行。單個操作是原子性的。多個操作也支持事務,但是不保證原子性,即失敗了的不影響其他成功的。通過MULTI和EXEC指令包起來。
- 豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性。
Q3:Redis也有事務嗎?能不能說一下Redis的事務?
Redis的事務本質就是一組命令的集合。事務支持一次執行多個命令,一個事務中所有命令都會被序列化。在事務執行過程,會按照順序串行化執行隊列中的命令,其他客戶端提交的命令請求不會插入到事務執行命令序列中。Redis單個命令保證原子性,一個事務里面多個命令就不保證了,即有成功有失敗。
Redis的事務只要分為三個階段:開始事務,命令入隊,執行事務。期間可以使用watch指令來對變量進行監控,watch類似樂觀鎖,如果watch監控的多個KEY中任何KEY的值已經被其他客戶端更改,則使用EXEC執行事務時,事務隊列將不會被執行,同時返回Nullmulti-bulk應答以通知調用者事務執行失敗。
Q4:Redis有豐富的特性,那么特性有哪些?
速度快;簡單穩定; 支持多種數據結構;支持多種編程語言;功能豐富;持久化;主從復制;高可用和分布式。好了,下面的問題基本也是圍繞這八個特性來問的。
Q5:Redis為什么速度那么快?
總結一句:Redis 使用了單線程架構和 IO 多路復用模型來實現高性能的內存數據庫服務。
擴展開來:① Redis使用純內存訪問,將所有數據放在內存中。② 非阻塞 IO,Redis 使用 epoll 作為 IO 多路復用技術的實現,再加上 Redis 本身的事件處理模型將 epoll 中的連接、讀寫、關閉都轉換為事件,不在網絡 IO 上浪費過多的時間。③ 單線程避免了線程切換和競爭產生的消耗。單線程的一個問題是對於每個命令的執行時間是有要求的,如果某個命令執行時間過長會造成其他命令的阻塞,對於 Redis 這種高性能服務來說是致命的,因此 Redis 是面向快速執行場景的數據庫。
補充:Redis的單線程指的是網絡請求模塊使用了一個線程(所以不需考慮並發安全性),即一個線程處理所有網絡請求,其他模塊仍用了多個線程。
Q6:為什么說Redis簡單穩定呢?是因為單線程嗎?
與單線程是有一定的關系的。之所以說Redis簡單穩定,主要是有三個方面的原因:
① 源碼很少,早期只有 2 萬行左右,在 3.0 版本由於添加了集群特性,增加到了 5 萬行左右,相對於很多 NoSQL 數據庫來說代碼量要少很多。
② 采用單線程模型,使得服務端處理模型更簡單,也使客戶端開發更簡單。
③ 不依賴底層操作系統的類庫,自己實現了事件處理的相關功能。
Q7:說一下Redis支持哪些數據類型,分別可以運用到什么場景?
Q8:Redis功能豐富,那么提供了哪些簡單的功能?
① 提供了鍵過期功能,可以實現緩存。
② 提供了發布訂閱功能,可以實現消息系統。
③ 支持 Lua 腳本,可以創造新的 Redis 命令。
④ 提供了簡單的事務功能,能在一定程度上保證事務特性。
⑤ 提供了流水線功能,客戶端能將一批命令一次性傳到 Redis,減少網絡開銷。
Q9:Redis是怎么進行持久化?
我們知道Redis的數據都存在內存里,如果突然宕機,數據就會全部丟失,因此必須有一種機制來保證Redis的數據不會因為故障而丟失,這種機制就是Redis的持久化機制。

Redis的持久化機制主要是有兩種,第一種是RDB快照,第二種是AOF日志。如果我們的服務器開啟了AOF持久化功能,那么服務器會優先使用AOF文件來還原數據庫的狀態。只有在AOF持久化功能處於關閉的狀態的時候,服務器才能使用RDB文件來還原數據庫狀態。
Q10:能說一下RDB持久化的原理嗎?
RDB持久化是通過快照來實現的,在指定的時間間隔內將內存的數據集快照寫入磁盤,恢復的時候就是將快照文件讀取到內存中。
(1)一般手動觸發可以使用save和bgsave命令:
save:阻塞當前 Redis 服務器,直到 RDB 過程完成為止,對於內存比較大的實例會造成長時間阻塞,不建議使用。
bgasve:Redis 進程執行 fork 操作創建子進程,RDB 持久化過程由子進程負責,完成后自動結束。阻塞只發生在 fork 階段,一般時間很短。bgsave 是針對 save 阻塞問題做的優化,因此 Redis 內部所有涉及 RDB 的操作都采用 bgsave 的方式,而 save 方式已經廢棄。所以RDB持久化主要原理要從bgsave這個命令來講。(見下一題)
(補充:可能會問到為什么需要fork一個子進程?這是因為Redis是單線程的,內存快照又要要求Redis進行文件IO操作。所以我們fork一個子進程就可以保證Redis主進程不進行IO操作,可以更好的保持極高的性能。)
(2)而其他一些通過其他命令也會被動觸發RDB持久化的也有:
FLUSHALL:清空命令也會觸發持久化操作,但dump.rdb文件中是空的,無意義;
SHUTDOWN :關閉數據庫命令也會觸發redis持久化操作 ,前提是沒有開啟AOF持久化;
DEBUG RELAOD:用該命令重新加載Redis時,也會自動觸發save操作;
(3)然后還有就是通過配置文件配置redis.conf文件中可以設置save。
Q11:那能講一下bgsave具體的工作流程嗎?
① 執行 bgsave 命令,Redis 父進程判斷當前是否存在正在執行的子進程,如 RDB/AOF 子進程,如果存在 bgsave 命令直接返回。
② 父進程執行 fork 操作創建子進程,fork 操作過程中父進程會阻塞。
③ 父進程 fork 完成后,bgsave 命令返回並不再阻塞父進程,可以繼續響應其他命令。
④ 子進程創建 RDB 文件,根據父進程內存生成臨時快照文件,完成后對原有文件進行原子替換。
⑤ 進程發送信號給父進程表示完成,父進程更新統計信息。
在這里詳細說一下第三個步驟里面父進程fork一個子進程之后父進程還要持續服務端的請求。然后對內存數據結構進行不間斷的修改。那我們不可能對同一片內存進行操作,所以用到了寫入時復制(copy on write機制)。在父進程需要后續再寫指令的時候,復制一個內存頁面處理新的寫請求,而子進程的頁面是沒有變化的,所以這也被稱為快照的原因。

Q12:說了挺多你能總結一下RDB到底有什么優缺點?
優點:
- 對數據的完整性要求不高
- 因為主進程不進行任何的IO操作,所以對數據的大規模恢復有着極高的性能,適合全量恢復 ,備份等場景
缺點:
- fork進程的時候,會占用一定的內存空間
- 需要一定的時間間隔來進行操作,如果redis意外宕機了,那么最后一次修改的數據就沒有了
Q13:能說一下AOF持久化的原理嗎?
AOF 持久化以獨立日志的方式記錄每次寫命令,重啟時再重新執行 AOF 文件中的命令達到恢復數據的目的。AOF 的主要作用是解決了數據持久化的實時性,目前是 Redis 持久化的主流方式。
默認是不開啟AOF的,如果開啟了,則優先使用AOF!
AOF的具體工作流程如下:
- 命令的實時寫入,調用到命令
- 所有的寫入命令追加到aof_buf(緩沖區)中
- AOF緩沖區根據對應的策略向硬盤做同步操作
- 隨着AOF文件越來越大,需要定期對AOF文件進行重寫,壓縮,父進程執行fork創建子進程,由子進程根據內存快照執行AOF重寫,父進行繼續響應后面的命令並寫入aof_rewrite_buf(重寫緩沖區),在子進程完成重寫后,重寫緩沖區再把新增的寫入命令寫入到新的AOF文件中。(注意,父進程原本的任務是沒有終止的,還是會進行原來而AOF文件的寫入同步)
- Redis服務重啟,加載AOF文件進行數據恢復

主動觸發可以使用bgrewriteaof命令,被動觸發需要修改配置文件兩個參數auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 來確定自動觸發時機。
Q14:AOF緩沖區,和AOF重寫緩沖區,兩者有什么區別?而且緩沖區有哪些對硬盤操作的同步的策略?
aof緩沖區:是正常使用aof作為數據落地中間地帶,所有的數據先到aof緩沖區再到aof文件中。
aof重寫緩沖區:是aof觸發重寫機制時,redis還要繼續接收數據,這個數據就寫到aof重寫緩沖區,當aof重寫ok時,主進程在把aof重寫緩沖區的數據寫到新的aof文件,然后替換原來的。
aof緩沖區同步策略,通過參數appendfsync控制,具體有三個配值
| 選項的值 | 說明 | 其他 |
|---|---|---|
| always | 命令寫入aof_buf后調用系統fsync操作同步到AOF文件,fsync完成后線程返回 | 每次寫入都要同步AOF文件,在一般的SATA硬盤很難達到高性能 |
| everysec | 命令寫入aof_buf后調用系統write操作,write完成后線程返回。fsync同步操作由線程每秒調用一次(建議策略) | 默認同步策略 |
| no | 命令寫入aof_buf后調用系統write操作,不對AOF文件做fsync同步,同步硬盤操作由操作系統負責,通常同步周期最長30秒 | 操作系統每次同步AOF文件的周期不可控,而且會加大每次同步硬盤的數據量,雖然提升了性能,但數據安全性無法保證 |
Q15:說了這么多重寫機制,那為什們重寫就可以把我們的AOF文件變小?
- 重寫的時候,進程內已經超時的數據不再寫入文件
- 舊的AOF文件含有無效命令,重寫使用進程內數據直接生成,新的AOF文件只保留最終數據的寫入命令
- 多條寫命令可以合並為一個,為了防止溢出,以64個元素為界拆分為多條
待更新
