Java面試題沖刺第二天--Redis篇
面試題1:為什么要用 Redis ?業(yè)務在哪塊兒用到的?
正經回答:
Redis是眼下最為人熟知的緩解高并發(fā)、提升高可用能力的手段之一,再提升服務器性能方面效果顯著。
這里不得不提到高并發(fā)場景,我們知道,并發(fā)場景下核心點在數(shù)據(jù)庫,引入緩存(以及引入任何負載均衡、集群等策略)的目的都是在減輕數(shù)據(jù)庫壓力,讓更多原本打到DB上的請求,在中間被攔截處理掉。就像你請個假屁大點兒事還要大老板簽字一樣?
通俗易懂點兒說,高并發(fā)對服務器來說,就好比你被人錘一拳,這拳頭可是硬的很,光著膀子的話一拳就給我干吐血。。那么我為了承受住這一拳?穿棉襖、穿護墊、穿…是吧,只要夠厚,我都以為你在給我撓癢癢~同理,Redis就是一件又厚又彈的棉襖。
話說回來,它有多厚多彈呢?操作緩存就是直接操作內存,速度相當快,直接操作緩存能夠承受的請求數(shù)是遠遠大于直接訪問數(shù)據(jù)庫的。
Redis優(yōu)勢:
- 讀寫性能優(yōu)異, Redis能讀的速度是110000次/s,寫的速度是81000次/s。
- 支持數(shù)據(jù)持久化,支持AOF和RDB兩種持久化方式。
- 支持事務,Redis的所有操作都是原子性的,同時Redis還支持對幾個操作合并后的原子性執(zhí)行。
- 數(shù)據(jù)結構豐富,除了支持string類型的value外還支持hash、set、zset、list等數(shù)據(jù)結構。
- 支持主從復制,主機會自動將數(shù)據(jù)同步到從機,可以進行讀寫分離。
- 支持大量集群節(jié)點。
假如用戶第一次訪問數(shù)據(jù)庫中的某些數(shù)據(jù)。這個過程會比較慢,因為是從硬盤上讀取的。將該用戶訪問的數(shù)據(jù)存在數(shù)Redis中,這樣下一次再訪問這些數(shù)據(jù)的時候就可以直接從緩存中獲取了。同樣,我們可以把數(shù)據(jù)庫中的部分數(shù)據(jù)轉移到緩存中去,這樣用戶的一部分請求會直接打到緩存而不是數(shù)據(jù)庫(即半路攔截掉了)。如果數(shù)據(jù)庫中的對應數(shù)據(jù)改變的之后,同步改變緩存中相應的數(shù)據(jù)即可!
在我們業(yè)務中,包括熱點詞查詢、一些實時排行榜數(shù)據(jù)、訪問量點贊量統(tǒng)計、Session共享等等都可以引入Redis來處理。
深入追問: 追問1:Redis里有哪些數(shù)據(jù)類型?
豐富的數(shù)據(jù)類型,Redis有8種數(shù)據(jù)類型,當然常用的主要是 String、Hash、List、Set、 SortSet 這5種類型,他們都是基于鍵值的方式組織數(shù)據(jù)。每一種數(shù)據(jù)類型提供了非常豐富的操作命令,可以滿足絕大部分需求,如果有特殊需求還能自己通過 lua 腳本自己創(chuàng)建新的命令(具備原子性);
追問2:Redis與Memcached有哪些區(qū)別?
兩者都是非關系型內存鍵值數(shù)據(jù)庫
,現(xiàn)在公司一般都是用 Redis 來實現(xiàn)緩存,為什么不用Memcached呢?
參數(shù) | Redis | Memcached |
---|---|---|
類型 | 1. 支持內存 2. 非關系型數(shù)據(jù)庫 | 1. 支持內存 2. 鍵值對形式 3. 緩存形式 |
數(shù)據(jù)存儲類型 | 1. String 2. List 3. Set 4. Hash 5. Sort Set | 1. 文本型 2. 二進制類型 |
附加功能 | 1. 發(fā)布/訂閱模式 2. 主從分區(qū) 3. 序列化支持 4. 腳本支持【Lua腳本】 | 多線程服務支持 |
網絡IO模型 | 單線程的多路 IO 復用模型 | 多線程,非阻塞IO模式 |
持久化支持 | 1. RDB 2. AOF | 不支持 |
集群模式 | 原生支持 cluster 模式,可以實現(xiàn)主從復制,讀寫分離 | 沒有原生的集群模式,需要依靠客戶端來實現(xiàn)往集群中分片寫入數(shù)據(jù) |
內存管理機制 | 在 Redis 中,并不是所有數(shù)據(jù)都一直存儲在內存中,可以將一些很久沒用的 value 交換到磁盤 | Memcached 的數(shù)據(jù)則會一直在內存中,Memcached 將內存分割成特定長度的塊來存儲數(shù)據(jù),以完全解決內存碎片的問題。 |
適用場景 | 復雜數(shù)據(jù)結構,有持久化,高可用需求,value存儲內容較大,最大512M | 純key-value,數(shù)據(jù)量非常大,并發(fā)量非常大的業(yè)務 |
- memcached所有的值均是簡單的字符串,redis作為其替代者,支持更為豐富的數(shù)據(jù)類型
- redis的速度比memcached快很多
- redis可以持久化數(shù)據(jù)到磁盤,這個很關鍵,宕機斷電不再是硬傷。
追問3:那Redis怎樣防止異常數(shù)據(jù)不丟失的?如何持久化?
RDB 持久化 (快照)
- 將某個時間點的所有數(shù)據(jù)生成快照,存放到硬盤上。當數(shù)據(jù)量很大時,會很慢。
- 可以將快照復制到其它服務器從而創(chuàng)建具有相同數(shù)據(jù)的服務器副本。
- 如果系統(tǒng)發(fā)生故障,將會丟失最后一次創(chuàng)建快照之后的數(shù)據(jù)。
AOF 持久化(即時更新)
- 將寫命令添加到 AOF 文件(Append Only File)的末尾。
- 使用 AOF 持久化需要設置同步選項,從而確保寫命令同步到磁盤文件上的時機。這是因為對文件進行寫入并不會馬上將內容同步到磁盤上,而是先存儲到緩沖區(qū),然后由操作系統(tǒng)決定什么時候同步到磁盤。
有以下同步選項(同步頻率):
always
每個寫命令都同步;everysec
每秒同步一次;no
讓操作系統(tǒng)來決定何時同步。everysec
選項比較合適,可以保證系統(tǒng)崩潰時只會丟失一秒左右的數(shù)據(jù),并且 Redis 每秒執(zhí)行一次同步對服務器性能幾乎沒有任何影響;
面試題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.
正經回答:
上面是Redis官網給的解釋(官方文檔鏈接),翻譯后簡單說,因為Redis的瓶頸不是CPU的運行速度,而往往是網絡帶寬和機器的內存大小。
再說了,單線程切換開銷小,容易實現(xiàn)。既然單線程容易實現(xiàn),而且CPU不會成為瓶頸,那就順理成章地采用單線程的方案,當然了,也是為了避免多線程存在的很多坑。對了,一個節(jié)點是一個單線程。
深入追問:
追問1:單線程只使用了單核CPU,太浪費,有什么辦法發(fā)揮多核CPU的性能嘛?
我們可以通過在單機開多個Redis 實例,我們一直在強調的單線程,只是在處理我們的網絡請求的時候只有一個線程來處理。實際上,一個正式的Redis Server運行的時候肯定是不止一個線程的,都是集群形式,多少多少個節(jié)點,所以實際環(huán)境中大家不用擔心這種問題。
面試題3:聊一下對緩存穿透、緩存擊穿、緩存雪崩的理解吧
正經回答:
- 緩存穿透:指
緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù)
,導致所有的請求都打到數(shù)據(jù)庫上,然后數(shù)據(jù)庫還查不到(如null),造成數(shù)據(jù)庫短時間線程數(shù)被打滿而導致其他服務阻塞,最終導致線上服務不可用,這種情況一般來自黑客同學。 - 緩存擊穿:指
緩存中沒有但數(shù)據(jù)庫中有的數(shù)據(jù)
(一般是熱點數(shù)據(jù)緩存時間到期),這時由于并發(fā)用戶特別多,同時讀緩存沒讀到數(shù)據(jù),又同時去數(shù)據(jù)庫去查,引起數(shù)據(jù)庫壓力瞬間增大,線上系統(tǒng)卡住。 - 緩存雪崩:
指緩存同一時間大面積的失效
,緩存擊穿升級版。
深入追問:
追問1:那你說一下針對緩存擊穿的解決方法
- 根據(jù)實際業(yè)務情況,在Redis中維護一個熱點數(shù)據(jù)表,批量設為永不過期(如top1000),并定時更新top1000數(shù)據(jù)。
- 加互斥鎖(mutex key)
互斥鎖 緩存擊穿后,多個線程會同時去查詢數(shù)據(jù)庫的這條數(shù)據(jù),那么我們可以在第一個查詢數(shù)據(jù)的請求上使用一個互斥鎖來鎖住它。其他的線程走到這一步拿不到鎖就等著,等第一個線程查詢到了數(shù)據(jù),然后做緩存。后面的線程進來發(fā)現(xiàn)已經有緩存了,就直接走緩存。
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ù)庫后寫入緩存"); // 從數(shù)據(jù)庫查詢數(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);// 小憩一會兒 return getData04();// 重試 } } } return result; }
總結
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關注腳本之家的更多內容!
相關文章
mybatis 插件: 打印 sql 及其執(zhí)行時間實現(xiàn)方法
下面小編就為大家?guī)硪黄猰ybatis 插件: 打印 sql 及其執(zhí)行時間實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06一文搞懂SpringBoot如何利用@Async實現(xiàn)異步調用
異步調用幾乎是處理高并發(fā),解決性能問題常用的手段,如何開啟異步調用?SpringBoot中提供了非常簡單的方式,就是一個注解@Async。今天我們重新認識一下@Async,以及注意事項2022-09-09Springboot項目全局異常統(tǒng)一處理案例代碼
最近在做項目時需要對異常進行全局統(tǒng)一處理,主要是一些分類入庫以及記錄日志等,因為項目是基于Springboot的,所以去網絡上找了一些博客文檔,然后再結合項目本身的一些特殊需求做了些許改造,現(xiàn)在記錄下來便于以后查看2023-01-01