什么是Redis?
Redis是一種基于鍵值對的NoSQL數據庫。
主要的特點是把數據放在內存中,讀寫速度相比于磁盤會快很多。
對于性能要求很高的場景,比如緩存熱點數據,防止接口爆刷,都會用到Redis
Redis還支持持久化,將緩存中的數據異步落盤,以便服務器宕機時恢復數據。
Redis和MySQL的區別?
- Redis是非關系型數據庫,MySQL是關系型數據庫
- Redis的數據存儲在內存,MySQL的數據主要存儲在磁盤
- Redis存儲的是鍵值對,MySQL數據以行和列的形式存儲。
實際開發中,Redis和MySQL都會使用到,查詢數據先用Redis,未命中再查MySQL并寫回Redis
項目里哪里用到了Redis?
用戶活躍榜使用了ZSet有序集合(score字段進行排序),作者白名單里用到了Set集合結構
用戶登錄后的Session,使用了字符串類型
通過Lua腳本封裝Redis的setnex命令來實現分布式鎖,保證在高并發場景下,熱點數據短時間內的高頻訪問不會擊穿MySQL,多個線程沒有從Redis中獲得數據時,只有一個線程成功獲取鎖并重建緩存。Lua腳本保證原子性,防止在設置過期時間前崩潰造成死鎖,要么都成功,要么都失敗,避免鎖永遠不被釋放。設置一個監聽線程,任務未完成時自動續期。
部署過Redis嗎?
我有在生產環境中部署過單機版Redis,先在官網下載源碼包后執行make && make install編譯安裝。安裝后編輯redis.conf文件,配置遠程訪問、設置密碼、限制內存、設置內存過期淘汰策略、開啟AOF持久化等。
Redis的高可用方案有部署過嗎?
有部署過哨兵機制,一主兩從的Redis實例,再加上三個哨兵節點監控它們,哨兵節點主要設置了故障轉移的判定條件和超時閾值。當主節點發生故障時,哨兵節點之間能夠自動協商并選出新的主節點,這個過程大概需要10-15秒。
另一個項目部署過集群方案,該項目數據量大并且增長快,需要水平擴展能力。我們部署了6個主節點,每個主節點配備一個從節點,形成了一個3主3從的初始集群。
使用集群節點的好處是能夠自動進行分片映射,可以簡單地增加節點來水平擴展集群容量。故障轉移也很快,通常在幾秒內完成。
Redis可以用來干什么?
主要用作緩存,把高頻訪問的數據放到Redis中,并通過設置過期時間來保證數據一致性,減輕數據庫訪問壓力。
秒殺接口可以通過Lua腳本實現令牌桶算法實現流量控制。(HMSET)
Redis中有哪些數據類型?
字符串、哈希、列表、集合、有序集合
詳細介紹下字符串?
最常見的數據類型,存儲文本、數字或二進制數據,最大容量是512MB,
適合存儲單個對象,比如驗證碼、token、計數等。
詳細介紹下列表?
列表是一個有序的元素集合,支持從頭部/尾部插入/刪除元素,常見于消息隊列或任務列表。
LRANGE key start end范圍查詢
詳細介紹下哈希?
key value的集合,適合存儲對象,比如商品信息,用戶信息。value = {name : 'zhangsan', age = 18}l
詳細介紹下集合?
集合是無序且不重復的,支持交集、并集操作,查詢效率能達到?O(1)
?級別,主要用于去重、標簽、共同好友等場景。
詳細介紹下有序集合?
按照score排序的有序集合,支持范圍查詢,適合排行榜或優先級隊列
詳細介紹下BitMap??
BitMap可以把一組二進制位緊湊地放到一組連續的內存空間中,每一位代表一個對象的狀態,比如是否簽到,是否活躍等?
詳細介紹下HyperLogLog?
用于估算集合中的唯一元素數量(基數)的概率性數據結構,僅用12KB的內存實現,誤差范圍為0.81%。
底層把每個元素哈希為一個二進制串,然后取前14位進行分組,放到16384個桶中,計算每組最大的前導0數量,最大前導0數量越多代表這個桶內的元素大概率越多,最后用一個公式近似推算總體基數,觀察稀有事件的頻率來推測總數。
詳細介紹下GEO?
GEO存儲地理位置信息,可以用來計算兩點之間的距離,某位置為中心的半斤內查找其他元素等
應用場景包括:附近的人或商家、計算外賣員與商家的距離、判斷用戶是否進入某個區域等
底層基于ZSet有序不重復集合實現,通過特定算法把經緯度轉換為score
為什么使用Hash類型而不是字符串類型序列化存儲??
Hash可以只讀取或修改某一個字段,String必須全部取出來反序列化后讀取
Redis為什么快??
內存
因為讀寫數據都是在內存中,內存的讀寫速度本來就比磁盤快好幾個數量級
異步持久化
即使Redis的數據需要持久化(RDB或AOF),也是異步或后臺進行的,不會阻塞讀寫操作
I/O多路復用
Redis采用了I/O多路復用技術和事件驅動模型,一個線程就能監聽成千上萬個連接。I/O多路復用線程會持續監聽注冊的連接(文件描述符),當有請求的事件就緒后,會被放入一個事件列表。Redis主線程可以主動一次性獲取所有的就緒事件,這些事件被有序地分發給預設的事件處理器,來執行相應的accept、read、write操作。
Linux下獲取就緒事件一般用epoll_wait(),macos一般用kqueue等。
多線程
Redis6.0之前,連接建立,請求讀取、響應發送以及命令執行都是在主線程中進行的,這樣可以避免多線程情況下的鎖競爭和上下文切換。網絡IO和命令執行是串行的,性能瓶頸主要來源于網絡IO的序列化。
Redis6.0之后,為了解決網絡IO的性能瓶頸,Redis引入了多線程機制,把網絡IO和命令執行分開,網絡IO交給線程池處理,命令執行仍然在主線程中,網絡IO的線程主要是負責解析請求以及序列化發送,這樣可以充分利用多核CPU的性能。
底層數據結構優化
比如String的底層數據結構動態字符串支持動態擴容、預分配冗余空間,能夠減少內存碎片和內存分配的開銷
能詳細說一下IO多路復用嗎?
IO 多路復用是一種允許單個進程同時監視多個文件描述符的技術,使得程序能夠高效處理多個并發連接而無需創建大量線程。
主要的實現機制包含select poll epoll kqueue IOCP等。
說說select、poll、epoll、kqueue和IOCP的區別?
select的缺點是單個進程能夠監控的文件描述符有限,只有1024個,并且每次調研都需要將文件描述符集合從用戶空間拷貝到內核空間,然后遍歷找出就緒的文件描述符后再拷貝回來,性能開銷大
poll的優點是沒有最大文件描述符數量的限制,不再用 BitsMap 來傳入 FD,取而代之的是動態數組 pollfd。但是每次調用仍然需要將文件描述符集合從用戶空間復制到內核空間,仍然需要遍歷,性能較差
epoll引入了事件驅動/內核態的回調機制,將文件描述符一次性注冊到內核的紅黑樹中,在內核中通過回調機制主動維護就緒事件列表,而不需要像之前一樣請求一次維護一次,更不需要全部拷貝,而是僅返回就緒的文件描述符列表,避免了遍歷和重復拷貝的開銷。
kqueue是BSD/macos下的IO多路復用機制,類似于epoll,支持大規模并發連接,使用事件驅動模型
IOCP是windows系統下的IO多路復用機制,關注的是IO操作是否已經完成而非事件就緒通知,完全的異步I/O
舉例說一下阻塞I/O和I/O多路復用的區別??
傳統的阻塞I/O是指線程/進程發起I/O請求后需阻塞等待內核準備就緒并且I/O操作完成后才能執行后續的代碼,一個線程/進程只能負責一個I/O連接,不具有并發能力
I/O多路復用是指一個線程通過文件描述符監聽多個socket連接,?通過一個系統調用就可以知曉所有就緒的事件,只要有一個事件就緒就可以開始執行,可以處理大量并發的I/O請求
Redis早期為什么要使用單線程?
- 為了避免上下文頻繁切換和鎖競爭,避免并發控制的復雜性
- Redis是I/O密集型而不是CPU密集型,主要受內存和網絡I/O的限制,而不是CPU的計算能力限制,多線程收益不明顯
- 單線程可以保證命令執行的原子性,無需額外的同步機制
所以單線程 + I/O多路復用機制已經足夠了
Redis6.0為什么要引入多線程?
Redis 6.0引入的多線程并不是指數據操作的命令執行是多線程的,而是指網絡I/O操作是在一個線程池中并發執行的。這是因為,雖然IO多路復用機制可以監控成千上萬個就緒事件,但是epoll_wait()獲取就緒事件列表之后還需要執行實際的網絡I/O操作,實際的read()和write()系統調用是非常耗時的,所以Redis 6.0之后就將IO線程從主線程中剝離了出來,可以并發執行請求的解析和數據的響應,大大解決單線程下網絡I/O操作的瓶頸問題
說說Redis的常用命令
- 操作字符串GET/SET/INCR
- 操作哈希HGET/HSET/HGETALL
- 操作列表LPUSH/LPOP/LRANGE
- 操作集合SADD/SISMEMBER
- 操作有序集合ZADD/ZRANGE/ZINCRBY
詳細說說set命令?
用于設置字符串的key,一般會覆蓋原的值,支持過期時間和條件寫入,一般用來實現分布式鎖,存儲token,延長Session時間等
SETNX僅不存在的時候設置,SETXX僅存在的時候覆蓋
sadd命令的時間復雜度是多少?
一次性添加N個元素就是O(N),只添加一個元素就是O(1)
incr命令了解嗎?
用于將指定鍵的值加一,是一個原子的命令。如果key不存在,先將值設置為0再加1。常用于網站訪問量,文章點贊數等場景
單線程的Redis QPS能達到多少?
10w