10000字超全Redis面试题,再也不怕被问住了!
Redis 在當(dāng)今的計(jì)算機(jī)行業(yè),可以說是使用的最為廣泛的內(nèi)存數(shù)據(jù)庫,幾乎所有的后端技術(shù)面試都會涉及到 Redis 相關(guān)的知識,正所謂知己知彼,百戰(zhàn)百勝。小白今天精心整理的超全的 Redis 面試題,希望可以幫助到在路上的你們~
什么是 Redis
Redis 是完全開源免費(fèi)的,遵守 BSD 協(xié)議,是一個(gè)高性能的 key-value 數(shù)據(jù)庫
是一個(gè)使用 C 語言開發(fā)的數(shù)據(jù)庫,不過與傳統(tǒng)數(shù)據(jù)庫不同的是 Redis 的數(shù)據(jù)是存在內(nèi)存中的,它是內(nèi)存數(shù)據(jù)庫,所以讀寫速度非常快,因此 Redis 被廣泛應(yīng)用于緩存方向
Redis 除了做緩存之外,也經(jīng)常用來做分布式鎖,甚至是消息隊(duì)列
Redis 還支持事務(wù) 、持久化、Lua 腳本、多種集群方案等,可以使用眾多復(fù)雜的業(yè)務(wù)場景
常見的分布式緩存技術(shù)
緩存使用的比較多的主要是 Memcached 和 Redis。不過現(xiàn)在使用 Memcached 做緩存的比較少
Memcached 是分布式緩存最開始興起的時(shí)候比較常用的方案,后來隨著 Redis 的發(fā)展,Redis 逐漸成為了人們的首選
分布式緩存主要解決的是單機(jī)緩存的容量受服務(wù)器限制并且無法保存通用信息的問題。因?yàn)楸镜鼐彺嬷辉诋?dāng)前服務(wù)里有效,比如如果你部署了兩個(gè)相同的服務(wù),他們兩者之間的緩存數(shù)據(jù)是無法共享的
Memcached 與 Redis 的異同
共同點(diǎn)
都是基于內(nèi)存的數(shù)據(jù)庫,一般都用來當(dāng)作緩存使用
都有過期策略
兩者的性能都非常高
異同點(diǎn)
Redis 支持更豐富的數(shù)據(jù)類型,Memcached 只支持最簡單的 k/v 數(shù)據(jù)類型,所有的值均是簡單的字符串
Redis 支持?jǐn)?shù)據(jù)的持久化,而 Memecache 把數(shù)據(jù)全部存在內(nèi)存之中,重啟之后數(shù)據(jù)丟失
Redis 有災(zāi)難恢復(fù)機(jī)制,因?yàn)榭梢园丫彺嬷械臄?shù)據(jù)持久化到磁盤上
Redis 在服務(wù)器內(nèi)存使用完之后,可以將不用的數(shù)據(jù)放到磁盤上。但 Memcached 在服務(wù)器內(nèi)存使用完之后,就會直接報(bào)異常
Memcached 沒有原生的集群模式,需要依靠客戶端來實(shí)現(xiàn)往集群中分片寫入數(shù)據(jù);但 Redis目前是原生支持 cluster 模式的
Memcached 是多線程,非阻塞 IO 復(fù)用的網(wǎng)絡(luò)模型;Redis 使用單線程的多路 IO 復(fù)用模型
Redis 支持發(fā)布訂閱模型、Lua 腳本、事務(wù)等功能,而 Memcached 不支持。并且,Redis 支持更多的編程語言
Memcached 過期數(shù)據(jù)的刪除策略只用了惰性刪除,而 Redis 同時(shí)使用了惰性刪除與定期刪除
Redis 的數(shù)據(jù)類型
Redis 支持五種數(shù)據(jù)類型:string(字符串),hash(哈希),list(列表),set(集合)及 zsetsorted set:有序集合)
實(shí)際中我們最為常用的還是 string 類型,不過還有幾種高級數(shù)據(jù)類型我們也需要了解掌握:BloomFilter,RedisSearch,Redis-ML,還有更高級的數(shù)據(jù)類型,BloomFilter,RedisSearch,Redis-ML 如果使用過,那么在面試官眼里都是加分項(xiàng)!
緩存數(shù)據(jù)的處理流程
緩存的簡單處理流程如下:
如果用戶請求的數(shù)據(jù)命中緩存,就直接返回
緩存中不存在的話,查看數(shù)據(jù)庫中是否存在
如果數(shù)據(jù)庫中存在,則更新緩存中的數(shù)據(jù)
如果數(shù)據(jù)庫中不存在,則返回空數(shù)據(jù)
上圖就是一個(gè)最為簡易的緩存流程圖,從圖中我們也可以看出在使用緩存時(shí)的一系列注意事項(xiàng),比如當(dāng)緩存失效時(shí),如何更好的保護(hù)數(shù)據(jù)庫,如何保證數(shù)據(jù)庫與緩存數(shù)據(jù)一致等
Redis 運(yùn)行模式(單進(jìn)程還是單線程)
Redis 是單線程,利用隊(duì)列技術(shù)將并發(fā)訪問變?yōu)榇性L問,消除了傳統(tǒng)數(shù)據(jù)庫串行控制的開銷問題
一個(gè)字符串能存儲的最大容量
512 M
為什么要使用緩存
高性能
將高頻訪問的數(shù)據(jù)放進(jìn)緩存,保證高頻操作可以快速響應(yīng),提高系統(tǒng)響應(yīng)速度和用戶體驗(yàn)
高并發(fā)
一般像 MySQL 這類數(shù)據(jù)庫的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 緩存之后很容易達(dá)到 10w+,甚至最高能達(dá)到 30w+(redis 集群的話會更高)
QPS(Query Per Second):服務(wù)器每秒可以執(zhí)行的查詢次數(shù)
所以增加緩存可以大大提高系統(tǒng)的并發(fā)能力
具體說說 Redis 可以做什么
緩存
緩存機(jī)制幾乎在所有的大型網(wǎng)站都有使用,合理地使用緩存不僅可以加 快數(shù)據(jù)的訪問速度,而且能夠有效地降低后端數(shù)據(jù)源的壓力。Redis提供了鍵值過期時(shí)間設(shè)置,并且也提供了靈活控制最大內(nèi)存和內(nèi)存溢出后的淘汰策略。可以這么說,一個(gè)合理的緩存設(shè)計(jì)能夠很好的為一個(gè)網(wǎng)站的穩(wěn)定保駕護(hù)航
排行榜系統(tǒng)
排行榜系統(tǒng)幾乎存在于所有的網(wǎng)站,例如按照熱度排名的排行榜,按照 發(fā)布時(shí)間的排行榜,按照各種復(fù)雜維度計(jì)算出的排行榜,Redis提供了列表和有序集合數(shù)據(jù)結(jié)構(gòu),合理地使用這些數(shù)據(jù)結(jié)構(gòu)可以很方便地構(gòu)建各種排行榜系統(tǒng)
計(jì)數(shù)器應(yīng)用
計(jì)數(shù)器在網(wǎng)站中的作用至關(guān)重要,例如視頻網(wǎng)站有播放數(shù)、電商網(wǎng)站有 瀏覽數(shù),為了保證數(shù)據(jù)的實(shí)時(shí)性,每一次播放和瀏覽都要做加1的操作,如果并發(fā)量很大對于傳統(tǒng)關(guān)系型數(shù)據(jù)的性能是一種挑戰(zhàn)。Redis天然支持計(jì)數(shù)功能而且計(jì)數(shù)的性能也非常好,可以說是計(jì)數(shù)器系統(tǒng)的重要選擇
社交網(wǎng)絡(luò)
贊/踩、粉絲、共同好友/喜好、推送、下拉刷新等是社交網(wǎng)站的必備功能,由于社交網(wǎng)站訪問量通常比較大,而且傳統(tǒng)的關(guān)系型數(shù)據(jù)不太適合保存這種類型的數(shù)據(jù),Redis 提供的數(shù)據(jù)結(jié)構(gòu)可以相對比較容易地實(shí)現(xiàn)這些功能
消息隊(duì)列系統(tǒng)
消息隊(duì)列系統(tǒng)可以說是一個(gè)大型網(wǎng)站的必備基礎(chǔ)組件,因?yàn)槠渚哂袠I(yè)務(wù) 解耦、非實(shí)時(shí)業(yè)務(wù)削峰等特性。Redis 提供了發(fā)布訂閱功能和阻塞隊(duì)列的功能,雖然和專業(yè)的消息隊(duì)列比還不夠足夠強(qiáng)大,但是對于一般的消息隊(duì)列功能基本可以滿足
哪些是 Redis 不可以(不擅長)做的
我們可以站在數(shù)據(jù)規(guī)模和數(shù)據(jù)冷熱的角度來進(jìn)行分析
站在數(shù)據(jù)規(guī)模的角度看,數(shù)據(jù)可以分為大規(guī)模數(shù)據(jù)和小規(guī)模數(shù)據(jù),我們 知道 Redis 的數(shù)據(jù)是存放在內(nèi)存中的,雖然現(xiàn)在內(nèi)存已經(jīng)足夠便宜,但是如果數(shù)據(jù)量非常大,例如每天有幾億的用戶行為數(shù)據(jù),使用 Redis 來存儲的話,基本上是個(gè)無底洞,經(jīng)濟(jì)成本相當(dāng)?shù)母?/p>
站在數(shù)據(jù)冷熱的角度看,數(shù)據(jù)分為熱數(shù)據(jù)和冷數(shù)據(jù),熱數(shù)據(jù)通常是指需 要頻繁操作的數(shù)據(jù),反之為冷數(shù)據(jù),例如對于視頻網(wǎng)站來說,視頻基本信息基本上在各個(gè)業(yè)務(wù)線都是經(jīng)常要操作的數(shù)據(jù),而用戶的觀看記錄不一定是經(jīng)常需要訪問的數(shù)據(jù),這里暫且不討論兩者數(shù)據(jù)規(guī)模的差異,單純站在數(shù)據(jù)冷熱的角度上看,視頻信息屬于熱數(shù)據(jù),用戶觀看記錄屬于冷數(shù)據(jù)。如果將這些冷數(shù)據(jù)放在 Redis 中,基本上是對于內(nèi)存的一種浪費(fèi),但是對于一些熱數(shù)據(jù)可以放在 Redis 中加速讀寫,也可以減輕后端存儲的負(fù)載,可以說是事半功倍
Redis 常見數(shù)據(jù)結(jié)構(gòu)與使用場景
String
String 數(shù)據(jù)結(jié)構(gòu)是簡單的 key-value 類型,也是我們平時(shí)使用的最多的數(shù)據(jù)類型
常用命令:set,get,strlen,exists,decr,incr,setex 等等
應(yīng)用場景:一般常用在需要計(jì)數(shù)的場景,比如用戶的訪問次數(shù)、熱點(diǎn)文章的點(diǎn)贊轉(zhuǎn)發(fā)數(shù)量等等
基本操作
127.0.0.1:6379> set key value # 設(shè)置 key-value 類型的值 OK 127.0.0.1:6379> get key # 根據(jù) key 獲得對應(yīng)的 value "value" 127.0.0.1:6379> exists key # 判斷某個(gè) key 是否存在 (integer) 1 127.0.0.1:6379> strlen key # 返回 key 所儲存的字符串值的長度 (integer) 5 127.0.0.1:6379> del key # 刪除某個(gè) key 對應(yīng)的值 (integer) 1 127.0.0.1:6379> get key (nil)Copy to clipboardErrorCopied批量設(shè)置
127.0.0.1:6379> mset key1 value1 key2 value2 # 批量設(shè)置 key-value 類型的值 OK 127.0.0.1:6379> mget key1 key2 # 批量獲取多個(gè) key 對應(yīng)的 value 1) "value1" 2) "value2"Copy to clipboardErrorCopied計(jì)數(shù)器設(shè)置
127.0.0.1:6379> set number 1 OK 127.0.0.1:6379> incr number # 將 key 中儲存的數(shù)字值增一 (integer) 2 127.0.0.1:6379> get number "2" 127.0.0.1:6379> decr number # 將 key 中儲存的數(shù)字值減一 (integer) 1 127.0.0.1:6379> get number "1"Copy to clipboardErrorCopied過期設(shè)置
127.0.0.1:6379> expire key 60 # 數(shù)據(jù)在 60s 后過期 (integer) 1 127.0.0.1:6379> setex key 60 value # 數(shù)據(jù)在 60s 后過期 (setex:[set] + [ex]pire) OK 127.0.0.1:6379> ttl key # 查看數(shù)據(jù)還有多久過期 (integer) 56Copy to clipboardErrorCopiedlist
list 即是鏈表,鏈表是一種非常常見的數(shù)據(jù)結(jié)構(gòu),特點(diǎn)是易于數(shù)據(jù)元素的插入和刪除并且可以靈活調(diào)整鏈表長度,但是鏈表的隨機(jī)訪問困難
常用命令:rpush,lpop,lpush,rpop,lrange,llen 等
應(yīng)用場景:發(fā)布與訂閱或者說消息隊(duì)列、慢查詢等
隊(duì)列和棧的實(shí)現(xiàn)
通過 rpush/lpop 實(shí)現(xiàn)隊(duì)列
127.0.0.1:6379> rpush myList value1 # 向 list 的頭部(右邊)添加元素 (integer) 1 127.0.0.1:6379> rpush myList value2 value3 # 向 list 的頭部(最右邊)添加多個(gè)元素 (integer) 3 127.0.0.1:6379> lpop myList # 將 list 的尾部(最左邊)元素取出 "value1" 127.0.0.1:6379> lrange myList 0 1 # 查看對應(yīng)下標(biāo)的 list 列表, 0 為 start,1為 end 1) "value2" 2) "value3" 127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒數(shù)第一 1) "value2" 2) "value3"Copy to clipboardErrorCopied通過 rpush/rpop 實(shí)現(xiàn)棧
127.0.0.1:6379> rpush myList2 value1 value2 value3 (integer) 3 127.0.0.1:6379> rpop myList2 # 將 list的頭部(最右邊)元素取出 "value3"Copy to clipboardErrorCopiedhash
hash 類似于 JDK1.8 前的 HashMap,內(nèi)部實(shí)現(xiàn)也差不多(數(shù)組 + 鏈表),不過 Redis 的 hash 做了更多優(yōu)化。另外 hash 是一個(gè) string 類型的 field 和 value 的映射表,特別適合用于存 儲對象,比如我們可以用 hash 數(shù)據(jù)結(jié)構(gòu)來存儲用戶信息,商品信息等等
常用命令:hset,hmset,hexists,hget,hgetall,hkeys,hvals 等
應(yīng)用場景:系統(tǒng)中對象數(shù)據(jù)的存儲
127.0.0.1:6379> hmset userInfoKey name "guide" description "dev" age "24" OK 127.0.0.1:6379> hexists userInfoKey name # 查看 key 對應(yīng)的 value 中指定的字段是否存在 (integer) 1 127.0.0.1:6379> hget userInfoKey name # 獲取存儲在哈希表中指定字段的值 "guide" 127.0.0.1:6379> hget userInfoKey age "24" 127.0.0.1:6379> hgetall userInfoKey # 獲取在哈希表中指定 key 的所有字段和值 1) "name" 2) "guide" 3) "description" 4) "dev" 5) "age" 6) "24" 127.0.0.1:6379> hkeys userInfoKey # 獲取 key 列表 1) "name" 2) "description" 3) "age" 127.0.0.1:6379> hvals userInfoKey # 獲取 value 列表 1) "guide" 2) "dev" 3) "24" 127.0.0.1:6379> hset userInfoKey name "GuideGeGe" # 修改某個(gè)字段對應(yīng)的值 127.0.0.1:6379> hget userInfoKey name "GuideGeGe"Copy to clipboardErrorCopiedset
set 類似于 Java 中的 HashSet,Redis 中的 set 類型是一種無序集合,集合中的元素沒有先后順序。當(dāng)你需要存儲一個(gè)列表數(shù)據(jù),又不希望出現(xiàn)重復(fù)數(shù)據(jù)時(shí),set 是一個(gè)很好的選擇,并且 set 提供了判斷某個(gè)成員是否在一個(gè) set 集合內(nèi)的重要接口,這個(gè)也是 list 所不能提供的。可以基于 set 輕易實(shí)現(xiàn)交集、并集、差集的操作。如:可以將一個(gè)用戶所有的關(guān)注人存在一個(gè)集合中,將其所有粉絲存在一個(gè)集合。Redis 可以非常方便的實(shí)現(xiàn)如共同關(guān)注、共同粉絲、共同喜好等功能
常用命令:sadd,spop,smembers,sismember,scard,sinterstore,sunion 等
應(yīng)用場景:需要存放的數(shù)據(jù)不能重復(fù)以及需要獲取多個(gè)數(shù)據(jù)源交集和并集等場景
127.0.0.1:6379> sadd mySet value1 value2 # 添加元素進(jìn)去 (integer) 2 127.0.0.1:6379> sadd mySet value1 # 不允許有重復(fù)元素 (integer) 0 127.0.0.1:6379> smembers mySet # 查看 set 中所有的元素 1) "value1" 2) "value2" 127.0.0.1:6379> scard mySet # 查看 set 的長度 (integer) 2 127.0.0.1:6379> sismember mySet value1 # 檢查某個(gè)元素是否存在set 中,只能接收單個(gè)元素 (integer) 1 127.0.0.1:6379> sadd mySet2 value2 value3 (integer) 2 127.0.0.1:6379> sinterstore mySet3 mySet mySet2 # 獲取 mySet 和 mySet2 的交集并存放 在 mySet3 中 (integer) 1 127.0.0.1:6379> smembers mySet3 1) "value2"Copy to clipboardErrorCopiedsorted set
和 set 相比,sorted set 增加了一個(gè)權(quán)重參數(shù) score,使得集合中的元素能夠按 score 進(jìn)行有序排列,還可以通過 score 的范圍來獲取元素的列表
常用命令:zadd,zcard,zscore,zrange,zrevrange,zrem 等
應(yīng)用場景:需要對數(shù)據(jù)根據(jù)某個(gè)權(quán)重進(jìn)行排序的場景。比如在直播系統(tǒng)中,實(shí)時(shí)排行信息包含直播間在線用戶列表,各種禮物排行榜,彈幕消息等信息
127.0.0.1:6379> zadd myZset 3.0 value1 # 添加元素到 sorted set 中 3.0 為權(quán)重 (integer) 1 127.0.0.1:6379> zadd myZset 2.0 value2 1.0 value3 # 一次添加多個(gè)元素 (integer) 2 127.0.0.1:6379> zcard myZset # 查看 sorted set 中的元素?cái)?shù)量 (integer) 3 127.0.0.1:6379> zscore myZset value1 # 查看某個(gè) value 的權(quán)重 "3" 127.0.0.1:6379> zrange myZset 0 -1 # 順序輸出某個(gè)范圍區(qū)間的元素,0 -1 表示輸出所有元素 1) "value3" 2) "value2" 3) "value1" 127.0.0.1:6379> zrange myZset 0 1 # 順序輸出某個(gè)范圍區(qū)間的元素,0 為 start 1 為 stop 1) "value3" 2) "value2" 127.0.0.1:6379> zrevrange myZset 0 1 # 逆序輸出某個(gè)范圍區(qū)間的元素,0 為 start 1 為 stop 1) "value1" 2) "value2"Copy to clipboardErrorCopiedbitmap
bitmap 存儲的是連續(xù)的二進(jìn)制數(shù)字(0 和 1),通過 bitmap, 只需要一個(gè) bit 位來表示某個(gè)元素對應(yīng)的值或者狀態(tài),key 就是對應(yīng)元素本身。我們知道 8 個(gè) bit 可以組成一個(gè) byte,所以 bitmap 本身會極大的節(jié)省儲存空間
常用命令:setbit 、 getbit 、 bitcount 、 bitop
應(yīng)用場景:適合需要保存狀態(tài)信息(比如是否簽到、是否登錄...)并需要進(jìn)一步對這些信息進(jìn)行分析的場景。比如用戶簽到情況、活躍用戶情況、用戶行為統(tǒng)計(jì)
# SETBIT 會返回之前位的值(默認(rèn)是 0)這里會生成 7 個(gè)位 127.0.0.1:6379> setbit mykey 7 1 (integer) 0 127.0.0.1:6379> setbit mykey 7 0 (integer) 1 127.0.0.1:6379> getbit mykey 7 (integer) 0 127.0.0.1:6379> setbit mykey 6 1 (integer) 0 127.0.0.1:6379> setbit mykey 8 1 (integer) 0 # 通過 bitcount 統(tǒng)計(jì)被被設(shè)置為 1 的位的數(shù)量。 127.0.0.1:6379> bitcount mykey (integer) 2Copy to clipboardErrorCopiedRedis 為什么不用多線程以及為什么 Redis6.0 之后又引入了多線程
使用單線程的原因
單線程編程更容易并且更容易維護(hù)
Redis 的性能瓶頸不在 CPU ,主要在內(nèi)存和網(wǎng)絡(luò)
多線程就會存在死鎖、線程上下文切換等問題,甚至?xí)绊懶阅?/p>
Redis6.0 引入多線程主要是為了提高網(wǎng)絡(luò) IO 讀寫性能,因?yàn)檫@個(gè)是 Redis 中的一個(gè)性能瓶頸
雖然 Redis6.0 引入了多線程,但是 Redis 的多線程只是在網(wǎng)絡(luò)數(shù)據(jù)的讀寫這類耗時(shí)操作上使用了,執(zhí)行命令仍然是單線程順序執(zhí)行
設(shè)置緩存過期的作用
因?yàn)閮?nèi)存是有限的,如果緩存中的所有數(shù)據(jù)都是一直保存的話,很快就會 Out of memory 了
Redis 中除了字符串類型有自己獨(dú)有設(shè)置過期時(shí)間的命令 setex 外,其他方法都需要依靠 expire 命令來設(shè)置過期時(shí)間 。另外 persist 命令可以移除一個(gè)鍵的過期時(shí)間
還有一種情況需要設(shè)置過期時(shí)間,當(dāng)我們的業(yè)務(wù)場景就是需要某個(gè)數(shù)據(jù)只在某一時(shí)間段內(nèi)存在,比如我們的短信驗(yàn)證碼可能只在 1 分鐘內(nèi)有效,用戶登錄的 token 可能只在 1 天內(nèi)有效,此時(shí)設(shè)置過期時(shí)間就能比較好的處理,如果使用傳統(tǒng)的數(shù)據(jù)庫來處理的話,一般都是自己判斷過期,這樣更麻煩并且性能要差很多
Redis 過期刪除策略
定時(shí)刪除:在設(shè)置鍵的過期時(shí)間的同時(shí),創(chuàng)建一個(gè)定時(shí)器 time,讓定時(shí)器在鍵的過期時(shí)間來臨時(shí),立即執(zhí)行對鍵的刪除操作
惰性刪除:放任鍵過期不管,但是每次從鍵空間中獲取鍵時(shí),都檢查取得的鍵是否過期,如果過期的話,就刪除該鍵;如果沒有過期,就返回該鍵
定期刪除:每隔一段時(shí)間程序就對數(shù)據(jù)庫進(jìn)行一次檢查,刪除里面的過期鍵。至于要刪除多少過期鍵,以及要檢查多少個(gè)數(shù)據(jù)庫,則由算法決定
更新策略對比
Redis 內(nèi)存淘汰機(jī)制
Redis 提供 6 種數(shù)據(jù)淘汰策略:
volatile-lru(least recently used):從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中挑選最近最少使用的數(shù)據(jù)淘汰
volatile-ttl:從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中挑選將要過期的數(shù)據(jù)淘汰
volatile-random:從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中任意選擇數(shù)據(jù)淘汰
allkeys-lru(least recently used):當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,移除最近最少使用的 key(這個(gè)是最常用的)
allkeys-random:從數(shù)據(jù)集(server.db[i].dict)中任意選擇數(shù)據(jù)淘汰
no-eviction:禁止驅(qū)逐數(shù)據(jù),也就是說當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),新寫入操作會報(bào)錯
4.0 版本后增加以下兩種:
volatile-lfu(least frequently used):從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中挑選最不經(jīng)常使用的數(shù)據(jù)淘汰
allkeys-lfu(least frequently used):當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時(shí),在鍵空間中,移除最不經(jīng)常使用的 key
使用策略的規(guī)則:
(1)如果數(shù)據(jù)呈現(xiàn)冪律分布,也就是一部分?jǐn)?shù)據(jù)訪問頻率高,一部分?jǐn)?shù)據(jù)訪問頻率低,則使用 allkeys-lru
(2)如果數(shù)據(jù)呈現(xiàn)平等分布,也就是所有的數(shù)據(jù)訪問頻率都相同,則使用 allkeys-random
Redis 持久化
Redis 的一種持久化方式叫快照(snapshotting,RDB),另一種方式是只追加文件(append-only file, AOF)
快照(snapshotting)持久化(RDB)
Redis 可以通過創(chuàng)建快照來獲得存儲在內(nèi)存里面的數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)上的副本。Redis 創(chuàng)建快照之后,可以對快照進(jìn)行備份,可以將快照復(fù)制到其他服務(wù)器從而創(chuàng)建具有相同數(shù)據(jù)的服務(wù)器副本(Redis 主從結(jié)構(gòu),主要用來提高 Redis 性能),還可以將快照留在原地以便重啟服務(wù)器的時(shí)候使用
AOF(append-only file)持久化
與快照持久化相比,AOF 持久化的實(shí)時(shí)性更好,因此已成為主流的持久化方案,是指所有的命令行記錄以 Redis 命令請求協(xié)議的格式完全持久化存儲)保存為 aof 文件
默認(rèn)沒有開啟 AOF,可以通過如下命令開啟
appendonly yesCopy to clipboardErrorCopied兩種持久化方案對比:
Redis 常見的性能問題
Master 最好不要寫內(nèi)存快照,如果 Master 寫內(nèi)存快照,save 命令調(diào)度 rdbSave 函數(shù),會阻塞主線程的工作,當(dāng)快照比較大時(shí)對性能影響會非常大,會間斷性暫停服務(wù)
如果數(shù)據(jù)比較重要,需要在某個(gè) Slave 開啟 AOF 備份數(shù)據(jù),策略設(shè)置為每秒同步一次
為了主從復(fù)制的速度和連接的穩(wěn)定性,Master 和 Slave 最好在同一個(gè)局域網(wǎng)
盡量避免在壓力很大的主庫上增加從庫
主從復(fù)制不要用圖狀結(jié)構(gòu),用單向鏈表結(jié)構(gòu)更為穩(wěn)定,即:Master <- Slave1<- Slave2 <- Slave3…這樣的結(jié)構(gòu)方便解決單點(diǎn)故障問題,實(shí)現(xiàn) Slave 對 Master 的替換。如果 Master 掛了,可以立刻啟用 Slave1 做 Master,其他不變
Redis 同步機(jī)制
Redis 可以使用主從同步,從從同步。第一次同步時(shí),主節(jié)點(diǎn)做一次 bgsave,并同時(shí)將后續(xù)修改操作記錄到內(nèi)存 buffer,待完成后將 rdb 文件全量同步到復(fù)制節(jié)點(diǎn),復(fù)制節(jié)點(diǎn)接收完成后將 rdb 鏡像加載到內(nèi)存。加載完成后,再通知主節(jié)點(diǎn)將期間修改的操作記錄同步到復(fù)制節(jié)點(diǎn)進(jìn)行重放就完成了同步過程
Redis 主從同步有新舊兩種方案,具體看下面圖示
老方案:
新方案:
Pipeline 的好處
可以將多次 IO 往返的時(shí)間縮減為一次,前提是 pipeline 執(zhí)行的指令之間沒有因果相關(guān)性。使用 redis-benchmark 進(jìn)行壓測的時(shí)候可以發(fā)現(xiàn)影響 Redis 的 QPS 峰值的一個(gè)重要因素是 pipeline 批次指令的數(shù)目
緩存穿透
緩存穿透說簡單點(diǎn)就是大量請求的 key 根本不存在于緩存中,導(dǎo)致請求直接到了數(shù)據(jù)庫上,根本沒有經(jīng)過緩存這一層。比如某個(gè)攻擊者故意制造我們緩存中不存在的 key 發(fā)起大量請求,導(dǎo)致大量請求落到數(shù)據(jù)庫上,使得數(shù)據(jù)庫由于性能的原因崩潰,導(dǎo)致系統(tǒng)異常
緩存穿透問題產(chǎn)生
解決辦法:
緩存無效 key
如果緩存和數(shù)據(jù)庫都查不到某個(gè) key 的數(shù)據(jù)就寫一個(gè)到 Redis 中去并設(shè)置過期時(shí)間,可以解決請求的 key 變化不頻繁的情況,如果攻擊者的惡意攻擊,每次構(gòu)建不同的請求 key,會導(dǎo)致 Redis 中緩存大量無效的 key 。很明顯,這種方案并不能從根本上解決此問題
使用布隆過濾器
布隆過濾器是一個(gè)非常神奇的數(shù)據(jù)結(jié)構(gòu),通過它我們可以非常方便地判斷一個(gè)給定數(shù)據(jù)是否存在于海量數(shù)據(jù)中
具體是這樣做的:把所有可能存在的請求的值都存放在布隆過濾器中,當(dāng)用戶請求過來,先判斷用戶發(fā)來的請求的值是否存在于布隆過濾器中。不存在的話,直接返回請求參數(shù)錯誤信息給客戶端,存在的話才會走下面的流程
緩存雪崩
緩存雪崩描述的就是這樣一個(gè)簡單的場景:緩存在同一時(shí)間大面積的失效,后面的請求都直接落到了數(shù)據(jù)庫上,造成數(shù)據(jù)庫短時(shí)間內(nèi)承受大量請求
還有一種緩存雪崩的場景是:有一些被大量訪問數(shù)據(jù)(熱點(diǎn)緩存)在某一時(shí)刻大面積失效,導(dǎo)致對應(yīng)的請求直接落到了數(shù)據(jù)庫上
以上兩種常見,就好比雪崩一樣,摧枯拉朽之勢,數(shù)據(jù)庫的壓力可想而知,可能直接就被這么多請求弄宕機(jī)了
解決辦法
針對 Redis 服務(wù)不可用的情況:
采用 Redis 集群,避免單機(jī)出現(xiàn)問題整個(gè)緩存服務(wù)都沒辦法使用
限流,避免同時(shí)處理大量的請求
針對熱點(diǎn)緩存失效的情況:
設(shè)置不同的失效時(shí)間比如隨機(jī)設(shè)置緩存的失效時(shí)間
緩存永不失效
緩存擊穿
如果緩存中的某個(gè)熱點(diǎn)數(shù)據(jù)過期了,此時(shí)大量的請求訪問了該熱點(diǎn)數(shù)據(jù),就無法從緩存中讀取,直接訪問數(shù)據(jù)庫,數(shù)據(jù)庫很容易就被高并發(fā)的請求沖垮,這就是緩存擊穿的問題
擊穿其實(shí)可以看做是雪崩的一個(gè)子集,解決方法一般有兩種,設(shè)置熱點(diǎn)數(shù)據(jù)永不過期和設(shè)置互斥鎖
所謂的互斥鎖,就是保證同一時(shí)間只有一個(gè)業(yè)務(wù)線程更新緩存,對于沒有獲取互斥鎖的請求,要么等待鎖釋放后重新讀取緩存,要么就返回空值或者默認(rèn)值
如何保證緩存和數(shù)據(jù)庫的數(shù)據(jù)一致性
其實(shí)這是一個(gè)非常龐大且復(fù)雜的問題,根本不是一兩句話能夠說清楚的,如果要完全規(guī)避一致性問題,那么整個(gè)系統(tǒng)也會變得非常復(fù)雜
一般來說可以進(jìn)行如下處理:更新 DB,然后刪除 cache
當(dāng)然此時(shí)有可能遇到更新 DB 成功,但是刪除 cache 失敗的情況,處理辦法大致有兩種:
緩存失效時(shí)間變短(不推薦,治標(biāo)不治本) :我們讓緩存數(shù)據(jù)的過期時(shí)間變短,這樣的話緩存就會從數(shù)據(jù)庫中加載數(shù)據(jù)。另外,這種解決辦法對于先操作緩存后操作數(shù)據(jù)庫的場景不適用
增加 cache 更新重試機(jī)制(常用):如果 cache 服務(wù)當(dāng)前不可用導(dǎo)致緩存刪除失敗的話,我們就隔一段時(shí)間進(jìn)行重試,重試次數(shù)可以自己定。如果多次重試還是失敗的話,我們可以把當(dāng)前更新失敗的 key 存入隊(duì)列中,等緩存服務(wù)可用之后,再將緩存中對應(yīng)的 key 刪除即可
簡單介紹 Redis 事物
Redis 事務(wù)可以一次執(zhí)行多個(gè)命令,并且?guī)в幸韵氯齻€(gè)重要的保證:
批量操作在發(fā)送 EXEC 命令前被放入隊(duì)列緩存
收到 EXEC 命令后進(jìn)入事務(wù)執(zhí)行,事務(wù)中任意命令執(zhí)行失敗,其余的命令依然被執(zhí)行
在事務(wù)執(zhí)行過程,其他客戶端提交的命令請求不會插入到事務(wù)執(zhí)行命令序列中
一個(gè)事務(wù)從開始到執(zhí)行會經(jīng)歷以下三個(gè)階段:
開始事務(wù)
命令入隊(duì)
執(zhí)行事務(wù)
命令:
DISCARD 取消事務(wù),放棄執(zhí)行事務(wù)塊內(nèi)的所有命令
EXEC 執(zhí)行所有事務(wù)塊內(nèi)的命令
MULTI 標(biāo)記一個(gè)事務(wù)塊的開始
UNWATCH 取消 WATCH 命令對所有 key 的監(jiān)視
WATCH key [key ...] 監(jiān)視一個(gè)(或多個(gè)) key ,如果在事務(wù)執(zhí)行之前這個(gè)(或這些) key 被其他命令所改動,那么事務(wù)將被打斷
Redis 集群
(1)Redis Sentinal 著眼于高可用,在 master 宕機(jī)時(shí)會自動將 slave 提升為 master,繼續(xù)提供服務(wù)
(2)Redis Cluster 著眼于擴(kuò)展性,在單個(gè) redis 內(nèi)存不足時(shí),使用 Cluster 進(jìn)行分片存儲
集群之間是異步復(fù)制的,最大節(jié)點(diǎn)個(gè)數(shù)為16384個(gè),Redis 集群目前無法做數(shù)據(jù)庫選擇,默認(rèn)在 0 數(shù)據(jù)庫
為了使在部分節(jié)點(diǎn)失敗或者大部分節(jié)點(diǎn)無法通信的情況下集群仍然可用,所以集群使用了主從復(fù)制模型,每個(gè)節(jié)點(diǎn)都會有 N-1 個(gè)復(fù)制品
Redis 并不能保證數(shù)據(jù)的強(qiáng)一致性,這意味著在實(shí)際中集群在特定的條件下可能會丟失寫操作
Redis 內(nèi)存優(yōu)化
盡可能使用散列表(hashes),散列表(是說散列表里面存儲的數(shù)少)使用的內(nèi)存非常小,所以你應(yīng)該盡可能的將你的數(shù)據(jù)模型抽象到一個(gè)散列表里面。比如你的 web 系統(tǒng)中有一個(gè)用戶對象,不要為這個(gè)用戶的名稱,姓氏,郵箱,密碼設(shè)置單獨(dú)的 key,而是應(yīng)該把這個(gè)用戶的所有信息存儲到一張散列表里面
一個(gè) Redis 實(shí)例最多能存放多少的 keys?各數(shù)據(jù)類型最多能存放多少元素
理論上 Redis 可以處理多達(dá) 232 的 keys,并且在實(shí)際中進(jìn)行了測試,每個(gè)實(shí)例至少存放了 2 億 5 千萬的 keys。任何 list、set、和 sorted set 都可以放 232 個(gè)元素。換句話說,Redis 的存儲極限是系統(tǒng)中的可用內(nèi)存值
如何保證 Redis 中的20W數(shù)據(jù)都是數(shù)據(jù)庫中200W數(shù)據(jù)的熱點(diǎn)數(shù)據(jù)
正確設(shè)置內(nèi)存淘汰策略,當(dāng) Redis 內(nèi)存數(shù)據(jù)集大小上升到一定大小的時(shí)候,就會施行數(shù)據(jù)淘汰策略,從而剩下的數(shù)據(jù)就是熱點(diǎn)數(shù)據(jù)
如何將10W個(gè)以某個(gè)固定前綴開頭的key全部找出
使用 keys 指令可以掃出指定模式的 key 列表,但是,keys 指令會導(dǎo)致線程阻塞一段時(shí)間,此時(shí)整個(gè) Redis 系統(tǒng)不再能接受其他指令
這個(gè)時(shí)候可以使用 scan 指令,scan 指令可以無阻塞的提取出指定模式的 key 列表,但是會有一定的重復(fù)概率,在客戶端做一次去重就可以了,但是整體所花費(fèi)的時(shí)間會比直接用 keys 指令長,但是安全性更高!
如果有大量的 key 需要設(shè)置同一時(shí)間過期,一般需要注意什么
如果大量的 key 過期時(shí)間設(shè)置的過于集中,到過期的那個(gè)時(shí)間點(diǎn),Redis 可能會出現(xiàn)短暫的卡頓現(xiàn)象。一般需要在時(shí)間上加一個(gè)隨機(jī)值,使得過期時(shí)間分散一些
Redis 分布式鎖
通過 SETNX 來爭搶鎖,再用 EXPIRE 給鎖加一個(gè)過期時(shí)間,當(dāng)然為了保證 SETNX 和 EXPIRE 原子性執(zhí)行,在 Redis 2.6.12 之后,Redis 擴(kuò)展了 SET 命令的參數(shù),用這一條命令就可以了:
127.0.0.1:6379> SET lock 1 EX 10 NX OK好了,這就是今天的內(nèi)容,如果你覺得對你有所幫助,就點(diǎn)贊+在看支持一下吧~
往期精彩回顧適合初學(xué)者入門人工智能的路線及資料下載中國大學(xué)慕課《機(jī)器學(xué)習(xí)》(黃海廣主講)機(jī)器學(xué)習(xí)及深度學(xué)習(xí)筆記等資料打印機(jī)器學(xué)習(xí)在線手冊深度學(xué)習(xí)筆記專輯《統(tǒng)計(jì)學(xué)習(xí)方法》的代碼復(fù)現(xiàn)專輯 AI基礎(chǔ)下載本站qq群955171419,加入微信群請掃碼:總結(jié)
以上是生活随笔為你收集整理的10000字超全Redis面试题,再也不怕被问住了!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Elasticsearch Curato
- 下一篇: win7电脑假死机怎么办