redis的優(yōu)點?
- 非???/li>
- 支持豐富的數(shù)據(jù)類型
- 操作具有原子性
Redis適用場景?
- 緩存:減輕查詢壓力,提升系統(tǒng)性能
- 分布式鎖:保證數(shù)據(jù)準(zhǔn)確性、避免不同節(jié)點重復(fù)工作
- Session共享:解決集群服務(wù)模式下,用戶單點登陸問題
- 排行榜:利用SortSet實現(xiàn)
- 消息隊列:可以利用List實現(xiàn)一個消息隊列
redis的數(shù)據(jù)結(jié)構(gòu)?
String、Hash、List、Set、SortedSet。
- String:redis中的字符串是一種動態(tài)字符串,可以修改,底層實現(xiàn)有點類似于 Java 中的 ArrayList。基本操作:set、get、mset、mget
- List:類似Java中的LinkedList,它是鏈表而不是數(shù)組,因此插入刪除非常快,但是搜索定位比較慢?;静僮鳎簉push、rpush、LPOP、RPOP、lrange
- Hash:當(dāng)于 Java 中的 HashMap,內(nèi)部實現(xiàn)也差不多類似,都是通過 "數(shù)組 + 鏈表" 的鏈地址法來解決部分 哈希沖突。大字典擴(kuò)容比較耗時,redis使用漸進(jìn)式rehash,即同時保留兩個字典,同時檢索兩個字典,當(dāng)rehash完成后,檢索新的字典,擴(kuò)容為原來的兩倍?;静僮鳎篐SET、HGET。HSET books java "think in java"
- set:相當(dāng)于 Java 語言中的 HashSet,它內(nèi)部的鍵值對是無序、唯一的。它的內(nèi)部實現(xiàn)相當(dāng)于一個特殊的字典,字典中所有的 value 都是一個值 NULL。
- hset:它類似于 Java 中 SortedSet 和 HashMap 的結(jié)合體,一方面它是一個 set,保證了內(nèi)部 value 的唯一性,另一方面它可以為每個 value 賦予一個 score 值,用來代表排序的權(quán)重。它由跳躍表實現(xiàn)的。常見操作:ZADD(添加)、ZRANGE(按score排名,取出范圍)、ZREVRANGE(按score倒序排名,取出范圍)、ZCARD(相當(dāng)于count)、ZSCORE(獲取指定的value的score)
如果有大量的key需要設(shè)置過期時間,需要注意什么?
如果大量的key過期時間設(shè)置的過于集中,到過期的那個時間點,Redis可能會出現(xiàn)短暫的卡頓現(xiàn)象。嚴(yán)重的話會出現(xiàn)緩存雪崩,我們一般需要在時間上加一個隨機(jī)值,使得過期時間分散一些。
那你使用過Redis分布式鎖嗎,怎么使用的?
先拿setnx來爭搶鎖,搶到之后,再用expire給鎖加一個過期時間防止鎖忘記了釋放,setnx指令可以同時指定過期時間。
如果時間過期后,線程還沒有執(zhí)行完成,我們可以使用redission框架來做分布式鎖,同時用看門狗機(jī)制,在定時檢查加鎖線程是否完成,如果沒有完成,自動給鎖續(xù)加鎖時間。
redis正在給線上的業(yè)務(wù)提供服務(wù),那使用keys指令會有什么問題?
Redis的單線程的。keys指令會導(dǎo)致線程阻塞一段時間,線上服務(wù)會停頓,直到指令執(zhí)行完畢,服務(wù)才能恢復(fù)。
這個時候可以使用scan指令,scan指令可以無阻塞的提取出指定模式的key列表,但是會有一定的重復(fù)概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用keys指令長。
使用過Redis怎么做異步消息隊列?
用list作為隊列結(jié)構(gòu),rpush生產(chǎn)消息,lpop消費消息。當(dāng)lpop沒有消息的時候,要適當(dāng)sleep一會再重試,或者用blpop阻塞到有消息到來。
Redis做異步消息隊列可以多次消費嗎?
可以用pub/sub主題模式,實現(xiàn)廣播。
Redis做pub/Sub的缺點?
redis作為pub/sub做消息廣播,有丟消息的風(fēng)險。
由于redis緩存保護(hù)機(jī)制,對于Pub/Sub客戶端,大小限制是8M,當(dāng)輸出緩沖區(qū)超過8M時,會關(guān)閉連接。持續(xù)性限制是,當(dāng)客戶端緩沖區(qū)大小持續(xù)60秒超過2M,關(guān)閉客戶端連接,導(dǎo)致消息丟失。
redis實現(xiàn)延時隊列?
使用sortedset,拿時間戳作為score,消息內(nèi)容作為key調(diào)用zadd來生產(chǎn)消息,消費者用zrangebyscore指令獲取N秒之前的數(shù)據(jù)輪詢進(jìn)行處理。
redis兩種持久化方式?
RDB做鏡像全量持久化,AOF做增量持久化。因為RDB會耗費較長時間,不夠?qū)崟r,在停機(jī)的時候會導(dǎo)致大量丟失數(shù)據(jù),所以需要AOF來配合使用。在redis實例重啟時,會使用RDB持久化文件重新構(gòu)建內(nèi)存,再使用AOF重放近期的操作指令來實現(xiàn)完整恢復(fù)重啟之前的狀態(tài)。
redis兩種持久化方式的優(yōu)缺點?
-
RDB:
- 優(yōu)點:RDB對redis性能影響非常小,在持久化數(shù)據(jù)時會fork一個子進(jìn)程進(jìn)行持久化,而且在恢復(fù)數(shù)據(jù)時比AOF快。
- 缺點:RDB是快照文件,默認(rèn)是五分鐘發(fā)生一次,如果redis實例掉電,可能發(fā)生五分鐘數(shù)據(jù)丟失。AOF丟失的數(shù)據(jù)會少很多。RDB文件如果過大,可能導(dǎo)致客戶端暫停幾毫秒甚至幾百毫秒。
-
AOP:
- 優(yōu)點:AOF可以基本不丟失數(shù)據(jù),三種持久化模式,同步寫回、每秒寫回、操作系統(tǒng)控制的寫回。由于是追加寫,所以寫入的性能很高。
- 缺點:AOF的數(shù)據(jù)文件比RDB還要大。
- 兩者怎么選擇?
- 由于兩者針對的場景不一樣,所以兩者都要使用,首先使用RDB對數(shù)據(jù)快速恢復(fù),然后使用AOF對恢復(fù)的數(shù)據(jù)進(jìn)行補(bǔ)全。
redis機(jī)器突然宕機(jī)會怎么樣?
取決于AOF日志sync屬性的配置,如果不要求性能,在每條寫指令時都sync一下磁盤,就不會丟失數(shù)據(jù)。但是在高性能的要求下每次都sync是不現(xiàn)實的,一般都使用定時sync,比如1s1次,這個時候最多就會丟失1s的數(shù)據(jù)。
RDB的原理是什么?
RDB有兩種持久化方式,同步的save會阻塞。異步的bgsave不會阻塞。
redis是使用bgsave + fork + copy on write 來實現(xiàn)的。首先使用異步持久化,然后fork出一個子進(jìn)程進(jìn)行持久化。fork子進(jìn)程后,會將主進(jìn)程的內(nèi)存權(quán)限設(shè)置為read-only,當(dāng)主進(jìn)程發(fā)生write時,會將寫的區(qū)域復(fù)制一份出來,寫新的區(qū)域,從而不影響子進(jìn)程復(fù)制。
redis 使用pipeline的好處?
不適用pipeline時,是一條命令一次執(zhí)行一條返回,需要一個rtt。使用pipeline可以在一個rtt中執(zhí)行多條命令。pipeline對應(yīng)可靠性要求高的系統(tǒng),每次寫入都要求返回的系統(tǒng)不適用。適用于批量寫入,且允許少量失敗的場景。
redis同步機(jī)制?
redis做主從同步分為兩部分,一部分是全量同步,一部分是增量同步。當(dāng)從節(jié)點連接加入集群后,會發(fā)起同步命令, 此時主節(jié)點會做一次bgsave,后續(xù)的操作會寫入內(nèi)存buffer中,然后將RDB文件全量同步給從節(jié)點,從節(jié)點將RDB鏡像加載到內(nèi)存中。加載完成后將修改期間的操作同步到子節(jié)點重放,完成同步。后續(xù)的增量數(shù)據(jù)通過AOF日志同步即可。
如果同步時網(wǎng)絡(luò)中斷,或者服務(wù)宕機(jī)了,重連后會把缺少的數(shù)據(jù)補(bǔ)齊。
redis集群方案?
基于高可用的 Redis Sentinal模式,在馬上宕機(jī)后,將slave提升為master繼續(xù)服務(wù)。
基于擴(kuò)展性的 Redis Cluster模式,在單個redis內(nèi)存不足時,使用Cluster進(jìn)行分片存儲。
redis 為什么那么快?
redis采用的是基于內(nèi)存的單線程模型的KV數(shù)據(jù)庫,由C語言編寫的,能提供10+萬的QPS。
- 完全基于內(nèi)存,絕大部分請求都是存粹的內(nèi)存操作,速度非???。
- 數(shù)據(jù)結(jié)構(gòu)簡單,對數(shù)據(jù)操作也簡單,redis中的數(shù)據(jù)結(jié)構(gòu)是專門設(shè)計的。
- 采用單線程,避免了不必要的上下文切換,也不存在多線程切換帶來的CPU消耗,不用考慮鎖,也不會出現(xiàn)死鎖等導(dǎo)致性能消耗。
- 使用多路I/O復(fù)用模式,非阻塞IO。
什么是上下文切換?
線程時間片用完或者執(zhí)行結(jié)束,會放棄CPU,此時執(zhí)行的上下文會被保存起來。新獲取到CPU資源的線程會將原來執(zhí)行中斷保存下來的的數(shù)據(jù)恢復(fù)到CPU中繼續(xù)執(zhí)行,這就是上下文切換。
單線程redis在多核服務(wù)器上是否很浪費?
是的,可以在單機(jī)上部署多個redis實例。
redis單機(jī)瓶頸怎么解決?
使用 cluster 集群部署,cluster 集群下掛載多個master節(jié)點,每個master下掛載多個slave節(jié)點。如果要進(jìn)行橫線擴(kuò)展,只需要添加master節(jié)點即可。
此時集群使用的是主從同步,讀寫分離。
講講sentinel哨兵機(jī)制?
哨兵模式要求至少有三個實例,一個master節(jié)點,兩個slave節(jié)點,每個節(jié)點上都有一個哨兵服務(wù)。master提供寫服務(wù),slave提供讀服務(wù),當(dāng)master接待宕機(jī),會將一個slave節(jié)點提升為master節(jié)點進(jìn)行,保證集群的高可用。
哨兵服務(wù)的組要作用:
- 集群監(jiān)控:監(jiān)控master、slave實例是否正常工作。
- 消息通知:如果某個redis實例有故障,哨兵服務(wù)負(fù)責(zé)發(fā)送報警消息給管理員。
- 故障轉(zhuǎn)移:如果master節(jié)點宕機(jī),會自動轉(zhuǎn)移到slave節(jié)點上。
- 配置中心:故障發(fā)生后,通知client客戶端新的master地址。
redis過期策略?
定期刪除:定期隨機(jī)抽取一些設(shè)置了過期時間的key,檢查是否過期,過期了就刪除。
惰性刪除:不主動刪除,當(dāng)有查詢時,檢查是否過期,沒有過期返回,過期了直接刪除。
redis的內(nèi)存淘汰機(jī)制?
- 返回錯誤(noeviction):當(dāng)內(nèi)存達(dá)到限制,再次嘗試申請內(nèi)存時,返回錯誤。
-
回收最少使用:
- allkeys-lru:嘗試在所有的keys里,回收最少使用的key。
- volatile-lru:嘗試在過期集合中,回收最少使用的key。
-
隨機(jī)回收:
- allkeys-random:嘗試在所有的keys里,隨機(jī)回收。
- volatile-random:嘗試在過期集合中,隨機(jī)回收。
- 回收在過期集合里存活時間短的key(volatile-ttl)
布隆過濾器?
布隆過濾器實際上是一個很長的二進(jìn)制向量和一系列隨機(jī)映射函數(shù)組成。作用是檢索一個元素是否在集合中。優(yōu)點是查詢的空間效率和時間效率很高,缺點是有一定的誤識別率和刪除困難。
原理:當(dāng)一個元素加入集合時,通過N個散列函數(shù)將元素映射成一個位數(shù)組中的N個點,把他們設(shè)置為1。檢索時,如果有任意一個點是0,那么被檢索元素一定不在集合中,否則這個元素可能在集合中。
缺點:
- 存在誤判,即所有散列位上都是1,但是查找的元素并不在容器中。如果布隆過濾器是黑名單,則可以建立一個白名單來存儲可能會誤判的元素。
- 刪除困難,bit位上存儲的是1,不能簡單直接置為0,可能會影響其他元素的判斷。
二進(jìn)制向量長度:插入元素個數(shù)和誤判率確定。
散列函數(shù)個數(shù):散列函數(shù)個數(shù)由于二進(jìn)制向量長度n /插入元素的個數(shù)的m共同決定?!?/p>
為什么需要分布式事務(wù)鎖?
在分布式或者集群環(huán)境下,避免不同節(jié)點重復(fù)相同的工作,比如多個節(jié)點發(fā)送多條消息、避免破壞數(shù)據(jù)的正確性,比如兩個節(jié)點同時操作一條數(shù)據(jù)。
java中常見的實現(xiàn)方式?
-
基于 MySQL 中的鎖:MySQL 本身有自帶的悲觀鎖
for update關(guān)鍵字。 - 基于 Zookeeper 有序節(jié)點:Zookeeper 允許臨時創(chuàng)建有序的子節(jié)點,這樣客戶端獲取節(jié)點列表時,就能夠當(dāng)前子節(jié)點列表中的序號判斷是否能夠獲得鎖。
-
基于 Redis 的單線程:由于 Redis 是單線程,所以命令會以串行的方式執(zhí)行,并且本身提供了像
SETNX(set if not exists)這樣的指令,本身具有互斥性。
什么緩存穿透?怎么解決?
查詢一個一定不存在的數(shù)據(jù),由于緩存沒有命中,會去查詢數(shù)據(jù),如果查不到數(shù)據(jù),則不會寫緩存,這導(dǎo)致每次請求都會到數(shù)據(jù)庫,造成緩存穿透。
解決辦法:
- 緩存空對象:如果查詢?yōu)榭?,仍然將空結(jié)果做短時間緩存。同時會導(dǎo)致緩存和存儲的數(shù)據(jù)有一段時間窗口不一致,緩存空后,又寫入了數(shù)據(jù)。此時可以在寫入時,去掉緩存中的空對象。
- 布隆過濾器:如果布隆過濾器判斷不存在的對象,一定不存在,從而避免緩存穿透。
什么是緩存雪崩?改如何解決?
如果緩存集中在一段時間內(nèi)失效,發(fā)生大量的緩存穿透,所有的查詢都落在數(shù)據(jù)庫上,造成了緩存雪崩。
解決辦法:
- 加鎖排隊:在緩存失效后,通過加鎖或者隊列來控制讀數(shù)據(jù)庫寫緩存的線程數(shù)量。比如對某個 key 只允許一個線程查詢數(shù)據(jù)和寫緩存,其他線程等待
- 數(shù)據(jù)預(yù)熱:可以通過緩存 reload 機(jī)制,預(yù)先去更新緩存,再即將發(fā)生大并發(fā)訪問前手動觸發(fā)加載緩存不同的 key,設(shè)置不同的過期時間,讓緩存失效的時間點盡量均勻
- 做二級緩存,或者雙緩存策略:Cache1 為原始緩存,Cache2 為拷貝緩存,Cache1 失效時,可以訪問 Cache2,Cache1 緩存失效時間設(shè)置為短期,Cache2 設(shè)置為長期。
- 緩存過期隨機(jī)值:在緩存的時候給過期時間加上一個隨機(jī)值,這樣就會大幅度的減少緩存在同一時間過期。
怎么保證緩存和數(shù)據(jù)庫數(shù)據(jù)的一致性?
- 設(shè)置合理的過期時間,避免直接修改數(shù)據(jù)庫。
- 新增、更改、刪除數(shù)據(jù)庫操作時同步更新 Redis,可以使用事物機(jī)制來保證數(shù)據(jù)的一致性。
什么是 RDB 內(nèi)存快照?
在 Redis 執(zhí)行「寫」指令過程中,內(nèi)存數(shù)據(jù)會一直變化。所謂的內(nèi)存快照,指的就是 Redis 內(nèi)存中的數(shù)據(jù)在某一刻的狀態(tài)數(shù)據(jù)。
在生成 RDB 期間,Redis 可以同時處理寫請求么?
可以的,Redis 使用操作系統(tǒng)的多進(jìn)程寫時復(fù)制技術(shù) COW(Copy On Write) 來實現(xiàn)快照持久化,保證數(shù)據(jù)一致性。
Redis 在持久化時會fork`一個子進(jìn)程,快照持久化完全交給子進(jìn)程來處理,父進(jìn)程繼續(xù)處理客戶端請求。
當(dāng)主線程執(zhí)行寫指令修改數(shù)據(jù)的時候,這個數(shù)據(jù)就會復(fù)制一份副本, bgsave 子進(jìn)程讀取這個副本數(shù)據(jù)寫到 RDB 文件。
這既保證了快照的完整性,也允許主線程同時對數(shù)據(jù)進(jìn)行修改,避免了對正常業(yè)務(wù)的影響。
如何實現(xiàn)數(shù)據(jù)盡可能少丟失又能兼顧性能呢?
使用混合持久化,將 rdb 文件的內(nèi)容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是自持久化開始到持久化結(jié)束的這段時間發(fā)生的增量 AOF 日志,通常這部分 AOF 日志很小。
于是在 Redis 重啟的時候,可以先加載 rdb 的內(nèi)容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重啟效率因此大幅得到提升。
什么是 Cluster 集群?
Redis 集群是一種分布式數(shù)據(jù)庫方案,集群通過分片來進(jìn)行數(shù)據(jù)管理,并提供復(fù)制和故障轉(zhuǎn)移功能。
將數(shù)據(jù)劃分為若干個slots,每個節(jié)點負(fù)責(zé)一部分槽位。槽位的信息存儲于每個節(jié)點中。
哈希槽又是如何映射到 Redis 實例上呢?
- 根據(jù)鍵值對的 key,使用 CRC16 算法,計算出一個 16 bit 的值
- 將 16 bit 的值對 16384 執(zhí)行取模,得到 0 ~ 16383 的數(shù)表示 key 對應(yīng)的哈希槽。
- 根據(jù)該槽信息定位到對應(yīng)的實例。
- Redis 的key是與hash槽綁定的,所以新實例加入后,只需要手動給實例指定hash槽即可,同時手動遷移數(shù)據(jù)。
Cluster 如何實現(xiàn)故障轉(zhuǎn)移?
- 一個節(jié)點發(fā)現(xiàn)某個節(jié)點失聯(lián)了,它會將這條信息向整個集群廣播,其它節(jié)點也就可以收到這點失聯(lián)信息。
- 如果一個節(jié)點收到了某個節(jié)點失聯(lián)的數(shù)量已經(jīng)達(dá)到了集群的大多數(shù),就可以標(biāo)記該節(jié)點為確定下線狀態(tài),然后向整個集群廣播,強(qiáng)迫其它節(jié)點也接收該節(jié)點已經(jīng)下線的事實。
- 對該失聯(lián)節(jié)點進(jìn)行主從切換。
Redis線程模型?
Redis基于Reactor模式開發(fā)了網(wǎng)絡(luò)事件處理器,這個處理器被稱為文件事件處理器。它的組成結(jié)構(gòu)為4部分:多個套接字、IO多路復(fù)用程序、文件事件分派器、事件處理器。因為文件事件分派器隊列的消費是單線程的,所以Redis才叫單線程模型。
- 文件事件處理器使用 I/O 多路復(fù)用程序來同時監(jiān)聽多個套接字, 并根據(jù)套接字目前執(zhí)行的任務(wù)來為套接字關(guān)聯(lián)不同的事件處理器。
- 當(dāng)被監(jiān)聽的套接字準(zhǔn)備好執(zhí)行連接應(yīng)答、讀取、寫入、關(guān)閉等操作時, 與操作相對應(yīng)的文件事件就會產(chǎn)生, 這時文件事件處理器就會調(diào)用套接字之前關(guān)聯(lián)好的事件處理器來處理這些事件。
雖然文件事件處理器以單線程方式運(yùn)行, 但通過使用 I/O 多路復(fù)用程序來監(jiān)聽多個套接字, 文件事件處理器既實現(xiàn)了高性能的網(wǎng)絡(luò)通信模型, 又可以很好地與 redis 服務(wù)器中其他同樣以單線程方式運(yùn)行的模塊進(jìn)行對接, 這保持了 Redis 內(nèi)部單線程設(shè)計的簡單性。
Redis事務(wù)的三個階段?
- 事務(wù)開始 MULTI
- 命令入隊
- 事務(wù)執(zhí)行 EXEC
Redis事務(wù)保證原子性嗎,支持回滾嗎?
Redis中,單條命令是原子性執(zhí)行的,但事務(wù)不保證原子性,且沒有回滾。事務(wù)中任意命令執(zhí)行失敗,其余的命令仍會被執(zhí)行。