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)兒說(shuō),高并發(fā)對(duì)服務(wù)器來(lái)說(shuō),就好比你被人錘一拳,這拳頭可是硬的很,光著膀子的話一拳就給我干吐血。。那么我為了承受住這一拳?穿棉襖、穿護(hù)墊、穿…是吧,只要夠厚,我都以為你在給我撓癢癢~同理,Redis就是一件又厚又彈的棉襖。
話說(shuō)回來(lái),它有多厚多彈呢?操作緩存就是直接操作內(nèi)存,速度相當(dāng)快,直接操作緩存能夠承受的請(qǐng)求數(shù)是遠(yuǎn)遠(yuǎn)大于直接訪問(wè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)。
假如用戶第一次訪問(wèn)數(shù)據(jù)庫(kù)中的某些數(shù)據(jù)。這個(gè)過(guò)程會(huì)比較慢,因?yàn)槭菑挠脖P上讀取的。將該用戶訪問(wèn)的數(shù)據(jù)存在數(shù)Redis中,這樣下一次再訪問(wèn)這些數(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ù)、訪問(wèn)量點(diǎn)贊量統(tǒng)計(jì)、Session共享等等都可以引入Redis來(lái)處理。
深入追問(wèn): 追問(wèn)1:Redis里有哪些數(shù)據(jù)類型?
豐富的數(shù)據(jù)類型,Redis有8種數(shù)據(jù)類型,當(dāng)然常用的主要是 String、Hash、List、Set、 SortSet 這5種類型,他們都是基于鍵值的方式組織數(shù)據(jù)。每一種數(shù)據(jù)類型提供了非常豐富的操作命令,可以滿足絕大部分需求,如果有特殊需求還能自己通過(guò) lua 腳本自己創(chuàng)建新的命令(具備原子性);
追問(wèn)2:Redis與Memcached有哪些區(qū)別?
兩者都是非關(guān)系型內(nèi)存鍵值數(shù)據(jù)庫(kù)
,現(xiàn)在公司一般都是用 Redis 來(lái)實(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ù)制,讀寫分離 | 沒(méi)有原生的集群模式,需要依靠客戶端來(lái)實(shí)現(xiàn)往集群中分片寫入數(shù)據(jù) |
內(nèi)存管理機(jī)制 | 在 Redis 中,并不是所有數(shù)據(jù)都一直存儲(chǔ)在內(nèi)存中,可以將一些很久沒(méi)用的 value 交換到磁盤 | Memcached 的數(shù)據(jù)則會(huì)一直在內(nèi)存中,Memcached 將內(nèi)存分割成特定長(zhǎng)度的塊來(lái)存儲(chǔ)數(shù)據(jù),以完全解決內(nèi)存碎片的問(wèn)題。 |
適用場(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ī)斷電不再是硬傷。
追問(wèn)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)來(lái)決定何時(shí)同步。everysec
選項(xiàng)比較合適,可以保證系統(tǒng)崩潰時(shí)只會(huì)丟失一秒左右的數(shù)據(jù),并且 Redis 每秒執(zhí)行一次同步對(duì)服務(wù)器性能幾乎沒(méi)有任何影響;
面試題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)單說(shuō),因?yàn)镽edis的瓶頸不是CPU的運(yùn)行速度,而往往是網(wǎng)絡(luò)帶寬和機(jī)器的內(nèi)存大小。
再說(shuō)了,單線程切換開銷小,容易實(shí)現(xiàn)。既然單線程容易實(shí)現(xiàn),而且CPU不會(huì)成為瓶頸,那就順理成章地采用單線程的方案,當(dāng)然了,也是為了避免多線程存在的很多坑。對(duì)了,一個(gè)節(jié)點(diǎn)是一個(gè)單線程。
深入追問(wèn):
追問(wèn)1:?jiǎn)尉€程只使用了單核CPU,太浪費(fèi),有什么辦法發(fā)揮多核CPU的性能嘛?
我們可以通過(guò)在單機(jī)開多個(gè)Redis 實(shí)例,我們一直在強(qiáng)調(diào)的單線程,只是在處理我們的網(wǎng)絡(luò)請(qǐng)求的時(shí)候只有一個(gè)線程來(lái)處理。實(shí)際上,一個(gè)正式的Redis Server運(yùn)行的時(shí)候肯定是不止一個(gè)線程的,都是集群形式,多少多少個(gè)節(jié)點(diǎn),所以實(shí)際環(huán)境中大家不用擔(dān)心這種問(wèn)題。
面試題3:聊一下對(duì)緩存穿透、緩存擊穿、緩存雪崩的理解吧
正經(jīng)回答:
- 緩存穿透:指
緩存和數(shù)據(jù)庫(kù)中都沒(méi)有的數(shù)據(jù)
,導(dǎo)致所有的請(qǐng)求都打到數(shù)據(jù)庫(kù)上,然后數(shù)據(jù)庫(kù)還查不到(如null),造成數(shù)據(jù)庫(kù)短時(shí)間線程數(shù)被打滿而導(dǎo)致其他服務(wù)阻塞,最終導(dǎo)致線上服務(wù)不可用,這種情況一般來(lái)自黑客同學(xué)。 - 緩存擊穿:指
緩存中沒(méi)有但數(shù)據(jù)庫(kù)中有的數(shù)據(jù)
(一般是熱點(diǎn)數(shù)據(jù)緩存時(shí)間到期),這時(shí)由于并發(fā)用戶特別多,同時(shí)讀緩存沒(méi)讀到數(shù)據(jù),又同時(shí)去數(shù)據(jù)庫(kù)去查,引起數(shù)據(jù)庫(kù)壓力瞬間增大,線上系統(tǒng)卡住。 - 緩存雪崩:
指緩存同一時(shí)間大面積的失效
,緩存擊穿升級(jí)版。
深入追問(wèn):
追問(wèn)1:那你說(shuō)一下針對(duì)緩存擊穿的解決方法
- 根據(jù)實(shí)際業(yè)務(wù)情況,在Redis中維護(hù)一個(gè)熱點(diǎn)數(shù)據(jù)表,批量設(shè)為永不過(guò)期(如top1000),并定時(shí)更新top1000數(shù)據(jù)。
- 加互斥鎖(mutex key)
互斥鎖 緩存擊穿后,多個(gè)線程會(huì)同時(shí)去查詢數(shù)據(jù)庫(kù)的這條數(shù)據(jù),那么我們可以在第一個(gè)查詢數(shù)據(jù)的請(qǐng)求上使用一個(gè)互斥鎖來(lái)鎖住它。其他的線程走到這一步拿不到鎖就等著,等第一個(gè)線程查詢到了數(shù)據(jù),然后做緩存。后面的線程進(jìn)來(lái)發(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("我沒(méi)拿到鎖,緩存也沒(méi)數(shù)據(jù),先小憩一下"); Thread.sleep(100);// 小憩一會(huì)兒 return getData04();// 重試 } } } return result; }
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Java實(shí)現(xiàn)HashMap排序方法的示例詳解
這篇文章主要通過(guò)一些示例為大家介紹了Java對(duì)HashMap進(jìn)行排序的方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解一下2022-05-05springboot訪問(wèn)404問(wèn)題的解決辦法
工作中遇到url404問(wèn)題,解決問(wèn)題的進(jìn)程比較崎嶇,寫篇文章記錄,下面這篇文章主要給大家介紹了關(guān)于springboot訪問(wèn)404問(wèn)題的解決辦法,文中通過(guò)圖文介紹的非常詳細(xì),要的朋友可以參考下2023-03-03mybatis 插件: 打印 sql 及其執(zhí)行時(shí)間實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇mybatis 插件: 打印 sql 及其執(zhí)行時(shí)間實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06詳細(xì)介紹使用Java調(diào)用Python的四種方法
這篇文章主要給大家介紹了關(guān)于使用Java調(diào)用Python的四種方法,每種方法根據(jù)實(shí)際項(xiàng)目需求有其適用場(chǎng)景,其中,推薦使用Runtime.getRuntime()方法,因?yàn)樗鼮楹?jiǎn)潔且易于實(shí)現(xiàn),需要的朋友可以參考下2024-10-10一文搞懂SpringBoot如何利用@Async實(shí)現(xiàn)異步調(diào)用
異步調(diào)用幾乎是處理高并發(fā),解決性能問(wèn)題常用的手段,如何開啟異步調(diào)用?SpringBoot中提供了非常簡(jiǎn)單的方式,就是一個(gè)注解@Async。今天我們重新認(rèn)識(shí)一下@Async,以及注意事項(xiàng)2022-09-09Springboot項(xiàng)目全局異常統(tǒng)一處理案例代碼
最近在做項(xiàng)目時(shí)需要對(duì)異常進(jìn)行全局統(tǒng)一處理,主要是一些分類入庫(kù)以及記錄日志等,因?yàn)轫?xiàng)目是基于Springboot的,所以去網(wǎng)絡(luò)上找了一些博客文檔,然后再結(jié)合項(xiàng)目本身的一些特殊需求做了些許改造,現(xiàn)在記錄下來(lái)便于以后查看2023-01-01Java對(duì)象數(shù)組的添加、刪除和遍歷代碼示例
在Java編程中,我們經(jīng)常需要對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行遍歷操作,并根據(jù)業(yè)務(wù)需求刪除部分元素,這篇文章主要給大家介紹了關(guān)于Java對(duì)象數(shù)組的添加、刪除和遍歷的相關(guān)資料,需要的朋友可以參考下2024-04-04