java redis 批量删除key_互联网大厂Java工程师面试指南——Redis篇
注意一下咯:更多關(guān)于Java集合、JVM、多線程并發(fā)、spring原理、微服務(wù)、Netty 與RPC 、Kafka、日記、設(shè)計模式、Java算法、數(shù)據(jù)庫、Zookeeper、分布式緩存、數(shù)據(jù)結(jié)構(gòu)面試解析等等可以去這個Github鏈接地址:https://github.com/ThinkingHan/Java-note 閱讀,覺得不錯的兄弟先點贊此文,再Star吧,感謝支持
作為一個后端開發(fā)人員,不只是要求開發(fā)人員需要掌握 Redis,也要求運維人員也要懂 Redis。由于 Redis 的運用廣泛,我們也知道它的重要性,至此面試中經(jīng)常被問到。用XMind畫了一張導(dǎo)圖記錄Redis的學習筆記和一些面試解析及視頻鏈接(源文件對部分節(jié)點有詳細備注和參考資料):
一、Redis數(shù)據(jù)結(jié)構(gòu)相關(guān)
1.Redis 支持的數(shù)據(jù)類型
String字符串
格式:set key valuestring類型是二進制安全的。意思是redis的string可以包含任何數(shù)據(jù)。比如jpg圖片或者序列化的對象 。
string類型是Redis最基本的數(shù)據(jù)類型,一個鍵最大能存儲512MB。
Hash(哈希)
格式: hmset name key1 value1 key2 value2Redis hash 是一個鍵值(key=>value)對集合。
Redis hash是一個string類型的field和value的映射表,hash特別適合用于存儲對象。
List(列表)
Redis 列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)格式:lpush name value在 key 對應(yīng) list 的頭部添加字符串元素格式:rpush name value
在 key 對應(yīng) list 的尾部添加字符串元素格式:lrem name index
key 對應(yīng) list 中刪除 count 個和 value 相同的元素格式:llen name
返回 key 對應(yīng) list 的長度
Set(集合)
格式:sadd name valueRedis的Set是string類型的無序集合。
集合是通過哈希表實現(xiàn)的,所以添加,刪除,查找的復(fù)雜度都是O(1)。
zset(sorted set:有序集合)
格式:zadd name score valueRedis zset 和 set 一樣也是string類型元素的集合,且不允許重復(fù)的成員。
不同的是每個元素都會關(guān)聯(lián)一個double類型的分數(shù)。redis正是通過分數(shù)來為集合中的成員進行從小到大的排序。
zset的成員是唯一的,但分數(shù)(score)卻可以重復(fù)。
2.Redis有哪些常用的命令?
Redis命令大全
3.Redis有哪些應(yīng)用場景
面試題:Redis的應(yīng)用場景核心設(shè)計,看完面試不在慌!
二、Redis事務(wù)
1.什么是事務(wù)
Redis 中的事務(wù)是一組命令的集合,是 Redis 的最小執(zhí)行單位,一個事務(wù)要么都執(zhí)行,要么都不執(zhí)行。帶有以下三個重要的保證:
Redis 事務(wù)的原理是先將屬于一個事務(wù)的命令發(fā)送給 Redis,然后依次執(zhí)行這些命令。
2.為什么redis事務(wù)不具備原子性
單個 Redis 命令的執(zhí)行是原子性的,但 Redis 沒有在事務(wù)上增加任何維持原子性的機制,所以 Redis 事務(wù)的執(zhí)行并不是原子性的。事務(wù)可以理解為一個打包的批量執(zhí)行腳本,但批量指令并非原子化的操作,中間某條指令的失敗不會導(dǎo)致前面已做指令的回滾,也不會造成后續(xù)的指令不做。
3. Redis 事務(wù)相關(guān)命令有哪些?
DISCARD:取消事務(wù),放棄執(zhí)行事務(wù)塊內(nèi)的所有命令。EXEC:執(zhí)行所有事務(wù)塊內(nèi)的命令。MULTI:標記一個事務(wù)塊的開始。WATCH:Redis Watch 命令用于監(jiān)視一個(或多個) key ,如果在事務(wù)執(zhí)行之前這個(或這些) key 被其他命令所改動,那么事務(wù)將被打斷UNWATCH :取消 WATCH 命令對所有 key 的監(jiān)視。三、Redis持久化和緩存管理
1.Redis持久化是什么?
用一句話可以將持久化概括為:將數(shù)據(jù)(如內(nèi)存中的對象)保存到可永久保存的存儲設(shè)備中。持久化的主要應(yīng)用是將內(nèi)存中的對象存儲在數(shù)據(jù)庫中,或者存儲在磁盤文件中、 XML 數(shù)據(jù)文件中等等。
也可以從如下兩個層面來理解持久化:應(yīng)用層:如果關(guān)閉( Close )你的應(yīng)用,然后重新啟動則先前的數(shù)據(jù)依然存在。系統(tǒng)層:如果關(guān)閉( Shut Down )你的系統(tǒng)(電腦),然后重新啟動則先前的數(shù)據(jù)依然存在。
2.Redis 持久化機制有哪些?
Redis 提供兩種方式進行持久化。RDB 持久化:原理是將 Reids 在內(nèi)存中的數(shù)據(jù)庫記錄定時 dump 到磁盤上的 RDB 持久化。AOF(append only file)持久化:原理是將 Redis 的操作日志以追加的方式寫入文件。3.Redis 持久化機制 AOF 和 RDB 有什么區(qū)別?
aof,rdb是兩種 redis持久化的機制。用于crash后,redis的恢復(fù)。rdb的特性如下:
- fork一個進程,遍歷hash table,利用copy on write,把整個db dump保存下來。
- save, shutdown, slave 命令會觸發(fā)這個操作。
- 粒度比較大,如果save, shutdown, slave 之前crash了,則中間的操作沒辦法恢復(fù)。
aof有如下特性:
- 把寫操作指令,持續(xù)的寫到一個類似日志文件里。(類似于從postgresql等數(shù)據(jù)庫導(dǎo)出sql一樣,只記錄寫操作)
- 粒度較小,crash之后,只有crash之前沒有來得及做日志的操作沒辦法恢復(fù)。
兩種區(qū)別就是,一個是持續(xù)的用日志記錄寫操作,crash后利用日志恢復(fù);一個是平時寫操作的時候不觸發(fā)寫,只有手動提交save命令,或者是關(guān)閉命令時,才觸發(fā)備份操作。
選擇的標準,就是看系統(tǒng)是愿意犧牲一些性能,換取更高的緩存一致性(aof),還是愿意寫操作頻繁的時候,不啟用備份來換取更高的性能,待手動運行save的時候,再做備份(rdb)。rdb這個就更有些 eventually consistent的意思了。
4.RDB和AOF 持久化機制的優(yōu)缺點
RDB優(yōu)點
- RDB 是緊湊的二進制文件,比較適合備份,全量復(fù)制等場景
- RDB 恢復(fù)數(shù)據(jù)遠快于 AOF
RDB缺點
- RDB 無法實現(xiàn)實時或者秒級持久化;
- 新老版本無法兼容 RDB 格式。
AOF優(yōu)點
- 可以更好地保護數(shù)據(jù)不丟失;
- appen-only 模式寫入性能比較高;
- 適合做災(zāi)難性的誤刪除緊急恢復(fù)。
AOF缺點:
- 對于同一份文件,AOF 文件要比 RDB 快照大;
- AOF 開啟后,會對寫的 QPS 有所影響,相對于 RDB 來說 寫 QPS 要下降;
- 數(shù)據(jù)庫恢復(fù)比較慢, 不合適做冷備。
5.Redis 淘汰策略有哪些?
Redis的內(nèi)存淘汰策略是指在Redis的用于緩存的內(nèi)存不足時,怎么處理需要新寫入且需要申請額外空間的數(shù)據(jù)。
Redis 的緩存淘汰策略有:
noeviction:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,新寫入操作會報錯。allkeys-lru:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中,移除最近最少使用的key。allkeys-random:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中,隨機移除某個key。volatile-lru:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中,移除最近最少使用的key。volatile-random:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中,隨機移除某個key。6.Redis 緩存失效策略有哪些?
定時過期策略
每個設(shè)置過期時間的key都需要創(chuàng)建一個定時器,到過期時間就會立即清除。該策略可以立即清除過期的數(shù)據(jù),對內(nèi)存很友好;但是會占用大量的CPU資源去處理過期的數(shù)據(jù),從而影響緩存的響應(yīng)時間和吞吐量。
惰性過期策略
只有當訪問一個key時,才會判斷該key是否已過期,過期則清除。該策略可以最大化地節(jié)省CPU資源,卻對內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問,從而不會被清除,占用大量內(nèi)存。
定期過期策略
每隔一定的時間,會掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key,并清除其中已過期的key。該策略是前兩者的一個折中方案。通過調(diào)整定時掃描的時間間隔和每次掃描的限定耗時,可以在不同情況下使得CPU和內(nèi)存資源達到最優(yōu)的平衡效果。
Redis中同時使用了惰性過期和定期過期兩種過期策略。
所謂定期刪除,指的是 redis 默認是每隔 100ms 就隨機抽取一些設(shè)置了過期時間的 key,檢查其是否過期,如果過期就刪除。
假設(shè) redis 里放了 10w 個 key,都設(shè)置了過期時間,你每隔幾百毫秒,就檢查 10w 個 key,那 redis 基本上就死了,cpu 負載會很高的,消耗在你的檢查過期 key 上了。注意,這里可不是每隔 100ms 就遍歷所有的設(shè)置過期時間的 key,那樣就是一場性能上的災(zāi)難。實際上 redis 是每隔 100ms 隨機抽取一些 key 來檢查和刪除的。
但是問題是,定期刪除可能會導(dǎo)致很多過期 key 到了時間并沒有被刪除掉,那咋整呢?所以就是惰性刪除了。這就是說,在你獲取某個 key 的時候,redis 會檢查一下 ,這個 key 如果設(shè)置了過期時間那么是否過期了?如果過期了此時就會刪除,不會給你返回任何東西。
獲取 key 的時候,如果此時 key 已經(jīng)過期,就刪除,不會返回任何東西。但是實際上這還是有問題的,如果定期刪除漏掉了很多過期 key,然后你也沒及時去查,也就沒走惰性刪除,此時會怎么樣?如果大量過期 key 堆積在內(nèi)存里,導(dǎo)致 redis 內(nèi)存塊耗盡了,咋整?答案是:走內(nèi)存淘汰機制。
四、Redis緩存異常方案
1.緩存雪崩
1.1、什么是緩存雪崩?
如果緩存集中在一段時間內(nèi)失效,發(fā)生大量的緩存穿透,所有的查詢都落在數(shù)據(jù)庫上,造成了緩存雪崩由于原有緩存失效,新緩存未到期間所有原本應(yīng)該訪問緩存的請求都去查詢數(shù)據(jù)庫了,而對數(shù)據(jù)庫CPU和內(nèi)存造成巨大壓力,嚴重的會造成數(shù)據(jù)庫宕機。
舉例來說, 我們在準備一項搶購的促銷運營活動,活動期間將帶來大量的商品信息、庫存等相關(guān)信息的查詢。 為了避免商品數(shù)據(jù)庫的壓力,將商品數(shù)據(jù)放入緩存中存儲。 不巧的是,搶購活動期間,大量的熱門商品緩存同時失效過期了,導(dǎo)致很大的查詢流量落到了數(shù)據(jù)庫之上。對于數(shù)據(jù)庫來說造成很大的壓力。
1.2、有什么解決方案來防止緩存雪崩?
1、加鎖排隊mutex互斥鎖解決,Redis的SETNX去set一個mutex key,當操作返回成功時,再進行加載數(shù)據(jù)庫的操作并回設(shè)緩存,否則,就重試整個get緩存的方法2、數(shù)據(jù)預(yù)熱
緩存預(yù)熱就是系統(tǒng)上線后,將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)。這樣就可以避免在用戶請求的時候,先查詢數(shù)據(jù)庫,然后再將數(shù)據(jù)緩存的問題。用戶直接查詢事先被預(yù)熱的緩存數(shù)據(jù)。可以通過緩存reload機制,預(yù)先去更新緩存,再即將發(fā)生大并發(fā)訪問前手動觸發(fā)加載緩存不同的key3、雙層緩存策略
C1為原始緩存,C2為拷貝緩存,C1失效時,可以訪問C2,C1緩存失效時間設(shè)置為短期,C2設(shè)置為長期4、定時更新緩存策略
實效性要求不高的緩存,容器啟動初始化加載,采用定時任務(wù)更新或移除緩存5、設(shè)置不同的過期時間,讓緩存失效的時間點盡量均勻
2.緩存預(yù)熱
2.1.什么是緩存預(yù)熱
緩存預(yù)熱就是系統(tǒng)上線后,將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)。這樣就可以避免在用戶請求的時候,先查詢數(shù)據(jù)庫,然后再將數(shù)據(jù)緩存的問題。用戶直接查詢事先被預(yù)熱的緩存數(shù)據(jù)。如圖所示:
如果不進行預(yù)熱, 那么 Redis 初識狀態(tài)數(shù)據(jù)為空,系統(tǒng)上線初期,對于高并發(fā)的流量,都會訪問到數(shù)據(jù)庫中, 對數(shù)據(jù)庫造成流量的壓力。
2.2.有什么解決方案?
3.緩存穿透
3.1、什么是緩存穿透?
緩存穿透是指用戶查詢數(shù)據(jù),在數(shù)據(jù)庫沒有,自然在緩存中也不會有。這樣就導(dǎo)致用戶查詢的時候,在緩存中找不到對應(yīng)key的value,每次都要去數(shù)據(jù)庫再查詢一遍,然后返回空(相當于進行了兩次無用的查詢)。這樣請求就繞過緩存直接查數(shù)據(jù)庫
3.2、有什么解決方案來防止緩存穿透?
1、緩存空對象簡單粗暴的方法,如果一個查詢返回的數(shù)據(jù)為空(不管是數(shù)據(jù)不存在,還是系統(tǒng)故障),我們?nèi)匀话堰@個空結(jié)果進行緩存,但它的過期時間會很短,最長不超過五分鐘。2、布隆過濾器優(yōu)勢:占用內(nèi)存空間很小,位存儲;性能特別高,使用key的hash判斷key存不存在
將所有可能存在的數(shù)據(jù)哈希到一個足夠大的bitmap中,一個一定不存在的數(shù)據(jù)會被 這個bitmap攔截掉,從而避免了對底層存儲系統(tǒng)的查詢壓力
4.緩存降級
降級的情況,就是緩存失效或者緩存服務(wù)掛掉的情況下,我們也不去訪問數(shù)據(jù)庫。我們直接訪問內(nèi)存部分數(shù)據(jù)緩存或者直接返回默認數(shù)據(jù)。
舉例來說:
對于應(yīng)用的首頁,一般是訪問量非常大的地方,首頁里面往往包含了部分推薦商品的展示信息。這些推薦商品都會放到緩存中進行存儲,同時我們?yōu)榱吮苊饩彺娴漠惓G闆r,對熱點商品數(shù)據(jù)也存儲到了內(nèi)存中。同時內(nèi)存中還保留了一些默認的商品信息。如下圖所示:降級一般是有損的操作,所以盡量減少降級對于業(yè)務(wù)的影響程度。
5.緩存擊穿
5.1、什么是緩存擊穿?
緩存擊穿是指緩存中沒有但數(shù)據(jù)庫中有的數(shù)據(jù)(一般是緩存時間到期),這時由于并發(fā)用戶特別多,同時讀緩存沒讀到數(shù)據(jù),又同時去數(shù)據(jù)庫去取數(shù)據(jù),引起數(shù)據(jù)庫壓力瞬間增大,造成過大壓力
**5.2、會帶來什么問題
會造成某一時刻數(shù)據(jù)庫請求量過大,壓力劇增
**5.3、如何解決
5.3.1.使用互斥鎖(mutex key)
這種解決方案思路比較簡單,就是只讓一個線程構(gòu)建緩存,其他線程等待構(gòu)建緩存的線程執(zhí)行完,重新從緩存獲取數(shù)據(jù)就可以了。 如果是單機,可以用synchronized或者lock來處理,如果是分布式環(huán)境可以用分布式鎖就可以了(分布式鎖,可以用memcache的add, redis的setnx, zookeeper的添加節(jié)點操作)。
5.3.2."永遠不過期"
5.3.3.緩存屏障
該方法類似于方法一:使用countDownLatch和atomicInteger.compareAndSet()方法實現(xiàn)輕量級鎖
class MyCache{private ConcurrentHashMap<String, String> map;private CountDownLatch countDownLatch;private AtomicInteger atomicInteger;public MyCache(ConcurrentHashMap<String, String> map, CountDownLatch countDownLatch,AtomicInteger atomicInteger) {this.map = map;this.countDownLatch = countDownLatch;this.atomicInteger = atomicInteger;}public String get(String key){String value = map.get(key);if (value != null){System.out.println(Thread.currentThread().getName()+"t 線程獲取value值 value="+value);return value;}// 如果沒獲取到值// 首先嘗試獲取token,然后去查詢db,初始化化緩存;// 如果沒有獲取到token,超時等待if (atomicInteger.compareAndSet(0,1)){System.out.println(Thread.currentThread().getName()+"t 線程獲取token");return null;}// 其他線程超時等待try {System.out.println(Thread.currentThread().getName()+"t 線程沒有獲取token,等待中。。。");countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}// 初始化緩存成功,等待線程被喚醒// 等待線程等待超時,自動喚醒System.out.println(Thread.currentThread().getName()+"t 線程被喚醒,獲取value ="+map.get("key"));return map.get(key);}public void put(String key, String value){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}map.put(key, value);// 更新狀態(tài)atomicInteger.compareAndSet(1, 2);// 通知其他線程countDownLatch.countDown();System.out.println();System.out.println(Thread.currentThread().getName()+"t 線程初始化緩存成功!value ="+map.get("key"));}}class MyThread implements Runnable{private MyCache myCache;public MyThread(MyCache myCache) {this.myCache = myCache;}@Overridepublic void run() {String value = myCache.get("key");if (value == null){myCache.put("key","value");}} }public class CountDownLatchDemo {public static void main(String[] args) {MyCache myCache = new MyCache(new ConcurrentHashMap<>(), new CountDownLatch(1), new AtomicInteger(0));MyThread myThread = new MyThread(myCache);ExecutorService executorService = Executors.newFixedThreadPool(5);for (int i = 0; i < 5; i++) {executorService.execute(myThread);}} }五、Redis集群架構(gòu)
1.實現(xiàn)主從復(fù)制的兩種方式
1.1slaveof命令
- 建立主從命令:slaveof ip port
- 取消主從命令:slaveof no one
1.2.redis.conf配置文件配置
- 格式:slaveof ip port
- 從節(jié)點只讀:slave-read-only yes #配置只讀
2.復(fù)制過程了解嗎,講一講
3.redis的主從結(jié)構(gòu)
主從結(jié)構(gòu)一是可以進行冗余備份,二是可以實現(xiàn)讀寫分離主從復(fù)制冗余備份(還可以稱為:主從復(fù)制,數(shù)據(jù)冗余,數(shù)據(jù)備份,可以實現(xiàn)容災(zāi)快速恢復(fù))
持久化保證了即使redis服務(wù)重啟也會丟失數(shù)據(jù),因為redis服務(wù)重啟后會將硬盤上持久化的數(shù)據(jù)恢復(fù)到內(nèi)存中,但是當redis服務(wù)器的硬盤損壞了可能會導(dǎo)致數(shù)據(jù)丟失,如果通過redis的主從復(fù)制機制就可以避免這種單點故障. 例如:我們搭建一個主叫做redis0,兩個從,分別叫做redis1和redis2,即使一臺redis服務(wù)器宕機其它兩臺redis服務(wù)也可以繼續(xù)提供服務(wù)。主redis中的數(shù)據(jù)和從redis上的數(shù)據(jù)保持實時同步,當主redis寫入數(shù)據(jù)時通過主從復(fù)制機制會復(fù)制到兩個從redis服務(wù)上。
1.一個Master可以有多個Slave,不僅主服務(wù)器可以有從服務(wù)器,從服務(wù)器也可以有自己的從服務(wù)器
2.復(fù)制在Master端是非阻塞模式的,這意味著即便是多個Slave執(zhí)行首次同步時,Master依然可以提供查詢服務(wù);
3.復(fù)制在Slave端也是非阻塞模式的:如果你在redis.conf做了設(shè)置,Slave在執(zhí)行首次同步的時候仍可以使用舊數(shù)據(jù)集提供查詢;你也可以配置為當Master與Slave失去聯(lián)系時,讓Slave返回客戶端一個錯誤提示;
4.當Slave要刪掉舊的數(shù)據(jù)集,并重新加載新版數(shù)據(jù)時,Slave會阻塞連接請求讀寫分離
主從架構(gòu)中,可以考慮關(guān)閉主服務(wù)器的數(shù)據(jù)持久化功能,只讓從服務(wù)器進行持久化,這樣可以提高主服務(wù)器的處理性能。從服務(wù)器通常被設(shè)置為只讀模式,這樣可以避免從服務(wù)器的數(shù)據(jù)被誤修改。
4.什么是Redis 慢查詢?怎么配置?
慢查詢就是指,系統(tǒng)執(zhí)行命令之后,計算統(tǒng)計每條指令執(zhí)行的時間,如果執(zhí)行時間超過設(shè)置的閾值,就說明該命令為慢指令。Redis 慢查詢配置參數(shù)為:slowlog-log-slower-than:設(shè)置慢查詢定義閾值,超過這個閾值就是慢查詢。單位為微妙,默認為 10000。
slowlog-max-len:慢查詢的日志列表的最大長度,當慢查詢?nèi)罩玖斜硖幱谧畲箝L度的時候,最早插入的一個命令將從列表中移除。
5.Redis 有哪些架構(gòu)模式?講講各自的特點
主從模式,一般是一個主節(jié)點,一或多個從節(jié)點,為了保證我們的主節(jié)點宕機后,數(shù)據(jù)不丟失,我們將主節(jié)點的數(shù)據(jù)備份到從節(jié)點,從節(jié)點并不進行實際操作,只做實時同步操作,并不能起到高并發(fā)的目的。
哨兵模式,一個哨兵集群和一組主從架構(gòu)組成。比主從更好的是當我們的主節(jié)點宕機以后,哨兵會主動選舉出一個主節(jié)點繼續(xù)向外提供服務(wù)。
集群架構(gòu),由很多個小主從聚集在一起,一起向外提供服務(wù)的,將16384個卡槽切分存儲,并不是一個強一致性的集群架構(gòu),每一個小主從節(jié)點內(nèi)會存在選舉機制,保證對外的高可用架構(gòu)。
六、Redis分布式鎖
有時候講出來的更通俗
Redis分布式鎖正確實現(xiàn)視頻講解
總結(jié)
以上是生活随笔為你收集整理的java redis 批量删除key_互联网大厂Java工程师面试指南——Redis篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hive解决数据倾斜问题_Hive数据倾
- 下一篇: java美元兑换,(Java实现) 美元