Java面試題沖刺第二天--Redis篇
面試題1:為什么要用 Redis ?業(yè)務(wù)在哪塊兒用到的?
正經(jīng)回答:
Redis是眼下最為人熟知的緩解高并發(fā)、提升高可用能力的手段之一,再提升服務(wù)器性能方面效果顯著。
這里不得不提到高并發(fā)場(chǎng)景,我們知道,并發(fā)場(chǎng)景下核心點(diǎn)在數(shù)據(jù)庫(kù),引入緩存(以及引入任何負(fù)載均衡、集群等策略)的目的都是在減輕數(shù)據(jù)庫(kù)壓力,讓更多原本打到DB上的請(qǐng)求,在中間被攔截處理掉。就像你請(qǐng)個(gè)假屁大點(diǎn)兒事還要大老板簽字一樣?

通俗易懂點(diǎn)兒說,高并發(fā)對(duì)服務(wù)器來說,就好比你被人錘一拳,這拳頭可是硬的很,光著膀子的話一拳就給我干吐血。。那么我為了承受住這一拳?穿棉襖、穿護(hù)墊、穿…是吧,只要夠厚,我都以為你在給我撓癢癢~同理,Redis就是一件又厚又彈的棉襖。
話說回來,它有多厚多彈呢?操作緩存就是直接操作內(nèi)存,速度相當(dāng)快,直接操作緩存能夠承受的請(qǐng)求數(shù)是遠(yuǎn)遠(yuǎn)大于直接訪問數(shù)據(jù)庫(kù)的。
Redis優(yōu)勢(shì):
- 讀寫性能優(yōu)異, Redis能讀的速度是110000次/s,寫的速度是81000次/s。
- 支持?jǐn)?shù)據(jù)持久化,支持AOF和RDB兩種持久化方式。
- 支持事務(wù),Redis的所有操作都是原子性的,同時(shí)Redis還支持對(duì)幾個(gè)操作合并后的原子性執(zhí)行。
- 數(shù)據(jù)結(jié)構(gòu)豐富,除了支持string類型的value外還支持hash、set、zset、list等數(shù)據(jù)結(jié)構(gòu)。
- 支持主從復(fù)制,主機(jī)會(huì)自動(dòng)將數(shù)據(jù)同步到從機(jī),可以進(jìn)行讀寫分離。
- 支持大量集群節(jié)點(diǎn)。
假如用戶第一次訪問數(shù)據(jù)庫(kù)中的某些數(shù)據(jù)。這個(gè)過程會(huì)比較慢,因?yàn)槭菑挠脖P上讀取的。將該用戶訪問的數(shù)據(jù)存在數(shù)Redis中,這樣下一次再訪問這些數(shù)據(jù)的時(shí)候就可以直接從緩存中獲取了。同樣,我們可以把數(shù)據(jù)庫(kù)中的部分?jǐn)?shù)據(jù)轉(zhuǎn)移到緩存中去,這樣用戶的一部分請(qǐng)求會(huì)直接打到緩存而不是數(shù)據(jù)庫(kù)(即半路攔截掉了)。如果數(shù)據(jù)庫(kù)中的對(duì)應(yīng)數(shù)據(jù)改變的之后,同步改變緩存中相應(yīng)的數(shù)據(jù)即可!
在我們業(yè)務(wù)中,包括熱點(diǎn)詞查詢、一些實(shí)時(shí)排行榜數(shù)據(jù)、訪問量點(diǎn)贊量統(tǒng)計(jì)、Session共享等等都可以引入Redis來處理。

深入追問: 追問1:Redis里有哪些數(shù)據(jù)類型?
豐富的數(shù)據(jù)類型,Redis有8種數(shù)據(jù)類型,當(dāng)然常用的主要是 String、Hash、List、Set、 SortSet 這5種類型,他們都是基于鍵值的方式組織數(shù)據(jù)。每一種數(shù)據(jù)類型提供了非常豐富的操作命令,可以滿足絕大部分需求,如果有特殊需求還能自己通過 lua 腳本自己創(chuàng)建新的命令(具備原子性);

追問2:Redis與Memcached有哪些區(qū)別?
兩者都是非關(guān)系型內(nèi)存鍵值數(shù)據(jù)庫(kù),現(xiàn)在公司一般都是用 Redis 來實(shí)現(xiàn)緩存,為什么不用Memcached呢?
| 參數(shù) | Redis | Memcached |
|---|---|---|
| 類型 | 1. 支持內(nèi)存 2. 非關(guān)系型數(shù)據(jù)庫(kù) | 1. 支持內(nèi)存 2. 鍵值對(duì)形式 3. 緩存形式 |
| 數(shù)據(jù)存儲(chǔ)類型 | 1. String 2. List 3. Set 4. Hash 5. Sort Set | 1. 文本型 2. 二進(jìn)制類型 |
| 附加功能 | 1. 發(fā)布/訂閱模式 2. 主從分區(qū) 3. 序列化支持 4. 腳本支持【Lua腳本】 | 多線程服務(wù)支持 |
| 網(wǎng)絡(luò)IO模型 | 單線程的多路 IO 復(fù)用模型 | 多線程,非阻塞IO模式 |
| 持久化支持 | 1. RDB 2. AOF | 不支持 |
| 集群模式 | 原生支持 cluster 模式,可以實(shí)現(xiàn)主從復(fù)制,讀寫分離 | 沒有原生的集群模式,需要依靠客戶端來實(shí)現(xiàn)往集群中分片寫入數(shù)據(jù) |
| 內(nèi)存管理機(jī)制 | 在 Redis 中,并不是所有數(shù)據(jù)都一直存儲(chǔ)在內(nèi)存中,可以將一些很久沒用的 value 交換到磁盤 | Memcached 的數(shù)據(jù)則會(huì)一直在內(nèi)存中,Memcached 將內(nèi)存分割成特定長(zhǎng)度的塊來存儲(chǔ)數(shù)據(jù),以完全解決內(nèi)存碎片的問題。 |
| 適用場(chǎng)景 | 復(fù)雜數(shù)據(jù)結(jié)構(gòu),有持久化,高可用需求,value存儲(chǔ)內(nèi)容較大,最大512M | 純key-value,數(shù)據(jù)量非常大,并發(fā)量非常大的業(yè)務(wù) |
- memcached所有的值均是簡(jiǎn)單的字符串,redis作為其替代者,支持更為豐富的數(shù)據(jù)類型
- redis的速度比memcached快很多
- redis可以持久化數(shù)據(jù)到磁盤,這個(gè)很關(guān)鍵,宕機(jī)斷電不再是硬傷。
追問3:那Redis怎樣防止異常數(shù)據(jù)不丟失的?如何持久化?
RDB 持久化 (快照)
- 將某個(gè)時(shí)間點(diǎn)的所有數(shù)據(jù)生成快照,存放到硬盤上。當(dāng)數(shù)據(jù)量很大時(shí),會(huì)很慢。
- 可以將快照復(fù)制到其它服務(wù)器從而創(chuàng)建具有相同數(shù)據(jù)的服務(wù)器副本。
- 如果系統(tǒng)發(fā)生故障,將會(huì)丟失最后一次創(chuàng)建快照之后的數(shù)據(jù)。
AOF 持久化(即時(shí)更新)
- 將寫命令添加到 AOF 文件(Append Only File)的末尾。
- 使用 AOF 持久化需要設(shè)置同步選項(xiàng),從而確保寫命令同步到磁盤文件上的時(shí)機(jī)。這是因?yàn)閷?duì)文件進(jìn)行寫入并不會(huì)馬上將內(nèi)容同步到磁盤上,而是先存儲(chǔ)到緩沖區(qū),然后由操作系統(tǒng)決定什么時(shí)候同步到磁盤。
有以下同步選項(xiàng)(同步頻率):
always每個(gè)寫命令都同步;everysec每秒同步一次;no讓操作系統(tǒng)來決定何時(shí)同步。everysec選項(xiàng)比較合適,可以保證系統(tǒng)崩潰時(shí)只會(huì)丟失一秒左右的數(shù)據(jù),并且 Redis 每秒執(zhí)行一次同步對(duì)服務(wù)器性能幾乎沒有任何影響;
面試題2:Redis為啥是單線程的?
Redis is single threaded. How can I exploit multiple CPU / cores? It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU. However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier. You can find more information about using multiple Redis instances in the Partitioning page. However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.
正經(jīng)回答:
上面是Redis官網(wǎng)給的解釋(官方文檔鏈接),翻譯后簡(jiǎn)單說,因?yàn)镽edis的瓶頸不是CPU的運(yùn)行速度,而往往是網(wǎng)絡(luò)帶寬和機(jī)器的內(nèi)存大小。再說了,單線程切換開銷小,容易實(shí)現(xiàn)。既然單線程容易實(shí)現(xiàn),而且CPU不會(huì)成為瓶頸,那就順理成章地采用單線程的方案,當(dāng)然了,也是為了避免多線程存在的很多坑。對(duì)了,一個(gè)節(jié)點(diǎn)是一個(gè)單線程。
深入追問:
追問1:?jiǎn)尉€程只使用了單核CPU,太浪費(fèi),有什么辦法發(fā)揮多核CPU的性能嘛?
我們可以通過在單機(jī)開多個(gè)Redis 實(shí)例,我們一直在強(qiáng)調(diào)的單線程,只是在處理我們的網(wǎng)絡(luò)請(qǐng)求的時(shí)候只有一個(gè)線程來處理。實(shí)際上,一個(gè)正式的Redis Server運(yùn)行的時(shí)候肯定是不止一個(gè)線程的,都是集群形式,多少多少個(gè)節(jié)點(diǎn),所以實(shí)際環(huán)境中大家不用擔(dān)心這種問題。
面試題3:聊一下對(duì)緩存穿透、緩存擊穿、緩存雪崩的理解吧
正經(jīng)回答:
- 緩存穿透:指
緩存和數(shù)據(jù)庫(kù)中都沒有的數(shù)據(jù),導(dǎo)致所有的請(qǐng)求都打到數(shù)據(jù)庫(kù)上,然后數(shù)據(jù)庫(kù)還查不到(如null),造成數(shù)據(jù)庫(kù)短時(shí)間線程數(shù)被打滿而導(dǎo)致其他服務(wù)阻塞,最終導(dǎo)致線上服務(wù)不可用,這種情況一般來自黑客同學(xué)。 - 緩存擊穿:指
緩存中沒有但數(shù)據(jù)庫(kù)中有的數(shù)據(jù)(一般是熱點(diǎn)數(shù)據(jù)緩存時(shí)間到期),這時(shí)由于并發(fā)用戶特別多,同時(shí)讀緩存沒讀到數(shù)據(jù),又同時(shí)去數(shù)據(jù)庫(kù)去查,引起數(shù)據(jù)庫(kù)壓力瞬間增大,線上系統(tǒng)卡住。 - 緩存雪崩:
指緩存同一時(shí)間大面積的失效,緩存擊穿升級(jí)版。
深入追問:
追問1:那你說一下針對(duì)緩存擊穿的解決方法

- 根據(jù)實(shí)際業(yè)務(wù)情況,在Redis中維護(hù)一個(gè)熱點(diǎn)數(shù)據(jù)表,批量設(shè)為永不過期(如top1000),并定時(shí)更新top1000數(shù)據(jù)。
- 加互斥鎖(mutex key)
互斥鎖 緩存擊穿后,多個(gè)線程會(huì)同時(shí)去查詢數(shù)據(jù)庫(kù)的這條數(shù)據(jù),那么我們可以在第一個(gè)查詢數(shù)據(jù)的請(qǐng)求上使用一個(gè)互斥鎖來鎖住它。其他的線程走到這一步拿不到鎖就等著,等第一個(gè)線程查詢到了數(shù)據(jù),然后做緩存。后面的線程進(jìn)來發(fā)現(xiàn)已經(jīng)有緩存了,就直接走緩存。
static Lock reenLock = new ReentrantLock();
public List<String> getData04() throws InterruptedException {
List<String> result = new ArrayList<String>();
// 從緩存讀取數(shù)據(jù)
result = getDataFromCache();
if (result.isEmpty()) {
if (reenLock.tryLock()) {
try {
System.out.println("拿到鎖了,從DB獲取數(shù)據(jù)庫(kù)后寫入緩存");
// 從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)
result = getDataFromDB();
// 將查詢到的數(shù)據(jù)寫入緩存
setDataToCache(result);
} finally {
reenLock.unlock();// 釋放鎖
}
} else {
result = getDataFromCache();// 先查一下緩存
if (result.isEmpty()) {
System.out.println("我沒拿到鎖,緩存也沒數(shù)據(jù),先小憩一下");
Thread.sleep(100);// 小憩一會(huì)兒
return getData04();// 重試
}
}
}
return result;
}
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
JAVA實(shí)現(xiàn)JSON后端向前端傳遞數(shù)據(jù)
本篇文章主要介紹了JAVA實(shí)現(xiàn)JSON后端向前端傳遞數(shù)據(jù),這里整理了詳細(xì)的代碼,具有一定的參考價(jià)值,有需要的小伙伴可以參考下。2017-03-03
java selenium XPath 定位實(shí)現(xiàn)方法
本文主要介紹java selenium XPath,這里整理了XPath的資料,并附實(shí)現(xiàn)方法,有需要的小伙伴可以參考下2016-08-08
SpringSecurity在單機(jī)環(huán)境下使用方法詳解
本文詳細(xì)介紹了SpringSecurity和SpringBoot的整合過程,包括配置用戶認(rèn)證、JSP頁(yè)面的使用、數(shù)據(jù)庫(kù)認(rèn)證以及授權(quán)功能的實(shí)現(xiàn),感興趣的朋友一起看看吧2025-02-02
JAVA基本類型包裝類 BigDecimal BigInteger 的使用
Java 中預(yù)定義了八種基本數(shù)據(jù)類型,包括:byte,int,long,double,float,boolean,char,short,接下來文章小編將向大家介紹其中幾個(gè)類型的內(nèi)容,需要的朋友可以參考下文章2021-09-09
java實(shí)現(xiàn)找出兩個(gè)文件中相同的單詞(兩種方法)
這篇文章主要介紹了java實(shí)現(xiàn)找出兩個(gè)文件中相同的單詞(兩種方法),需要的朋友可以參考下2020-08-08
解決springboot mapper注入報(bào)紅問題
這篇文章主要介紹了解決springboot mapper注入報(bào)紅問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
MyBatis3傳遞多個(gè)參數(shù)(Multiple Parameters)
這篇文章主要介紹了MyBatis3傳遞多個(gè)參數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java實(shí)現(xiàn)簡(jiǎn)單的飛機(jī)大戰(zhàn)游戲(控制主飛機(jī)篇)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單的飛機(jī)大戰(zhàn)游戲,控制主飛機(jī),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05

