redis核心面試題二(實戰優化)

文章目錄

  • 10. redis配置mysql實戰優化[重要]
  • 11. redis之緩存擊穿、緩存穿透、緩存雪崩
  • 12. redis實現分布式session

10. redis配置mysql實戰優化[重要]

// 最初實現@Override@Transactionalpublic Product createProduct(Product product) {productRepo.saveAndFlush(product);jedis.set(SystemConstants.REDIS_KEY_PREFIX + product.getProductId(), gson.toJson(product))return product;}@Override@Transactionalpublic Product updateProduct(Product product) {productRepo.saveAndFlush(product);jedis.set(SystemConstants.REDIS_KEY_PREFIX + product.getProductId(), gson.toJson(product))return product;}@Overridepublic Product getProduct(Long productId) {// 1. 先查redisString productRedis = jedis.get(SystemConstants.REDIS_KEY_PREFIX + productId);if (!StringUtil.isBlank(productRedis)) {return gson.fromJson(productRedis, Product.class);}// 2. redis沒有,再查mysql數據庫Product productMysql = productRepo.findByProductId(productId);if (productMysql != null) {// 3. 數據庫有,則更新redis數據jedis.set(SystemConstants.REDIS_KEY_PREFIX + productMysql.getProductId(), gson.toJson(productMysql));}// 4. 返回mysql數據庫數據return productMysql;}
小公司并發量不大的情況下,問題不是很大,但是大公司高并發量,會出現大量問題,列舉如下:
存在的問題:1. 緩存容量小問題:幾百G的海量數據不可能一直都放到redis緩存中,大大降低redis(<10G)作為內存數據庫的效率解決方案:設置固定過期時間,比如說一天,雖然一開始redis數據量很大,但是一天之后,會有大量數據失效,達到冷熱數據的分離。
jedis.set(SystemConstants.REDIS_KEY_PREFIX + product.getProductId(), gson.toJson(product));
jedis.expire(SystemConstants.REDIS_KEY_PREFIX + product.getProductId(), SystemConstants.REDIS_KEY_EXPIRED_TIME);2. 緩存擊穿問題:雖然設置了過期時間,仍然會出現緩存擊穿問題, 即單個熱點key失效的瞬間,持續的大并發請求就會擊破緩存,直接請求到數據庫,好像蠻力擊穿一樣(緩存無數據/數據庫有數據)解決方案:設置隨機過期時間
jedis.expire(SystemConstants.REDIS_KEY_PREFIX + productId, genRandomExpiredTime(5));
public Integer genRandomExpiredTime(Integer random) {return SystemConstants.REDIS_KEY_EXPIRED_TIME + new Random().nextInt(random) * 60 * 60;
}3. 緩存穿透問題:用戶訪問的數據既不在緩存當中,也不在數據庫中,按道理說數據庫都沒有這個數據,就不能一直來查數據庫了,防止黑客惡意攻擊。解決方案一:緩存空值(null)或默認值 + 過期時間在數據庫查詢不存在時,將其緩存為空值(null)或默認值,緩存失效時間一般設置為5分鐘之內,當數據庫被寫入或更新該key的新數據時,緩存必須同時被刷新,避免數據不一致。@Overridepublic Product getProduct(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;// 1. 先查redisString productRedis = jedis.get(redisId);if (!StringUtil.isBlank(productRedis)) {// 判斷緩存是否是默認值,避免緩存穿透if (productRedis.equals(SystemConstants.REDIS_DEFAULT_CACHE)) {jedis.expire(redisId, genRandomExpiredTime(3));return null;}jedis.expire(redisId, genRandomExpiredTime(5));return gson.fromJson(productRedis, Product.class);}// 2. redis沒有,再查mysql數據庫Product productMysql = productRepo.findByProductId(productId);if (productMysql != null) {// 3. 數據庫有,則更新redis數據jedis.set(redisId, gson.toJson(productMysql));jedis.expire(redisId, genRandomExpiredTime(5));} else {// 緩存空或默認值 + 過期時間,避免緩存穿透jedis.set(redisId, SystemConstants.REDIS_DEFAULT_CACHE);jedis.expire(redisId, genRandomExpiredTime(3));}return productMysql;}4. 突發性熱點緩存重建導致數據庫系統壓力倍增:也就是說某一數據本來是冷數據,存儲在數據庫中,突然出現大量訪問,redis還沒緩存該數據,因此需要大量查詢數據庫并重建緩存,也就是以下代碼重復執行,要是只執行一次就好了。if (!StringUtil.isBlank(productRedis)) {// 3. 數據庫有,則更新redis數據jedis.set(redisId, gson.toJson(productMysql));jedis.expire(redisId, genRandomExpiredTime(5));}解決方案一:DCL雙端檢鎖機制
但仍然存在以下問題,一方面synchronized鎖住的是單個JVM,若是該web項目集群部署,則在每個JVM都需要鎖一次,另一方面,假如productId=101是熱點數據會被鎖住,但是其他數據productId=202也需要排隊等待,效率降低。解決方案二:分布式鎖setnx
但仍然存在redis緩存和mysql數據庫數據不一致問題解決方案三:鎖優化-讀寫鎖5. 緩存雪崩:在使用緩存時,通常會對緩存設置過期時間,一方面目的是保持緩存與數據庫數據的一致性,另一方面是減少冷緩存占用過多的內存空間。但當緩存中大量熱點緩存在某一個時刻同時實效,請求全部轉發到數據庫,從而導致數據庫壓力驟增,造成系統崩潰等情況,這就是緩存雪崩。解決方案:1. key均勻失效:   將key的過期時間后面加上一個隨機數(比如隨機1-5分鐘),讓key均勻的失效。2. 雙key策略:		主key設置過期時間,備key不設置過期時間,當主key失效時,直接返回備key值。3. 構建緩存高可用集群
// 解決方案一:DCL雙端檢鎖機制
@Overridepublic Product getProduct(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;// 1. 先查redisString productRedis = jedis.get(redisId);if (!StringUtil.isBlank(productRedis)) {// 判斷緩存默認值,避免緩存穿透if (productRedis.equals(SystemConstants.REDIS_DEFAULT_CACHE)) {jedis.expire(redisId, genRandomExpiredTime(3));return null;}jedis.expire(redisId, genRandomExpiredTime(5));return gson.fromJson(productRedis, Product.class);}Product productMysql = null;synchronized (this) {// 2. DCL再查redis,因為只要有一次查詢數據庫操作,redis就已經有緩存數據了productRedis = jedis.get(redisId);if (!StringUtil.isBlank(productRedis)) {if (productRedis.equals(SystemConstants.REDIS_DEFAULT_CACHE)) {jedis.expire(redisId, genRandomExpiredTime(3));return null;}jedis.expire(redisId, genRandomExpiredTime(5));return gson.fromJson(productRedis, Product.class);}// 3. redis還是沒有,再查mysql數據庫productMysql = productRepo.findByProductId(productId);if (productMysql != null) {// 4. 數據庫有,則更新redis數據【可能出現突發性熱點緩存重建導致數據庫系統壓力倍增】jedis.set(redisId, gson.toJson(productMysql));jedis.expire(redisId, genRandomExpiredTime(5));} else {// 緩存空或默認值 + 過期時間,避免緩存穿透jedis.set(redisId, SystemConstants.REDIS_DEFAULT_CACHE);jedis.expire(redisId, genRandomExpiredTime(3));}}return productMysql;}
// 解決方案二:分布式鎖setnx<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.20.0</version></dependency>@Configurationpublic class RedissonConfig {@Beanpublic Redisson redisson() {Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);return (Redisson) Redisson.create(config);}}// 集群部署:分布式鎖public Product getProduct2(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;// 1. 先查redis緩存Product product = getProductFromRedis(redisId);if (product != null) {return product;}// 分布式鎖RLock確保鎖住特定的productId,不影響其他productId,解決所有問題RLock lock = redisson.getLock(SystemConstants.LOCK_HOT_CACHE_PREFIX + productId);lock.lock(); // 等價于setnx(SystemConstants.LOCK_HOT_CACHE_PREFIX + productId, value)// 2. DCL再查redis,因為只要有一次查詢數據庫操作,redis就已經有緩存數據了Product productMysql = null;try {product = getProductFromRedis(redisId);if (product != null) {return product;}// 3. redis還是沒有,最后查mysql數據庫productMysql = getProductFromMysql(productId);} finally {lock.unlock();}return productMysql;}private Product getProductFromRedis(String redisId) {Product product = null;String productRedis = jedis.get(redisId);if (!StringUtil.isBlank(productRedis)) {if (productRedis.equals(SystemConstants.REDIS_DEFAULT_CACHE)) {// 緩存中存在,卻是緩存默認值,也就是數據庫沒有數據,設置過期時間,避免緩存穿透jedis.expire(redisId, genRandomExpiredTime(3));return new Product(); // 特殊情況}// 緩存中存在,也是正常值jedis.expire(redisId, genRandomExpiredTime(5));product = gson.fromJson(productRedis, Product.class);}return product;}private Product getProductFromMysql(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;Product productMysql = productRepo.findByProductId(productId);if (productMysql != null) {// 數據庫有,則同步更新redis緩存數據【但是可能出現突發性熱點緩存重建導致數據庫系統壓力倍增,也就是這段代碼大量執行】jedis.set(redisId, gson.toJson(productMysql));jedis.expire(redisId, genRandomExpiredTime(5));} else {// 數據庫沒有,則設置默認值緩存 + 過期時間,避免緩存穿透jedis.set(redisId, SystemConstants.REDIS_DEFAULT_CACHE);jedis.expire(redisId, genRandomExpiredTime(3));}return productMysql;}
// 解決方案三:鎖優化-讀寫鎖
public Product getProductByReadWriteLock(Long productId) {String redisId = SystemConstants.REDIS_KEY_PREFIX + productId;// 1. 先查redis緩存Product product = getProductFromRedis(redisId);if (product != null) {return product;}// 加寫鎖ReadWriteLock readWriteLock = redisson.getReadWriteLock(SystemConstants.LOCK_HOT_UPDATE_PREFIX + productId);Lock writeLock = readWriteLock.writeLock();writeLock.lock();// 2. DCL再查redis,因為只要有一次查詢數據庫操作,redis就已經有緩存數據了Product productMysql;try {product = getProductFromRedis(redisId);if (product != null) {return product;}// 3. 加讀鎖 讀數據庫ReadWriteLock readWriteLock2 = redisson.getReadWriteLock(SystemConstants.LOCK_HOT_UPDATE_PREFIX + productId);Lock readLock = readWriteLock2.readLock();readLock.lock();productMysql = getProductFromMysql(productId);readLock.unlock();} finally {writeLock.unlock();}return productMysql;}  

11. redis之緩存擊穿、緩存穿透、緩存雪崩

  • 緩存擊穿-緩存無數據/數據庫有數據

單個熱點key失效的瞬間,持續的大并發請求就會擊破緩存,直接請求到數據庫,好像蠻力擊穿一樣。這種情況就是緩存擊穿(Cache Breakdown)。

在這里插入圖片描述

1. 使用互斥鎖(Mutex Key)只讓一個線程構建緩存,其他線程等待構建緩存執行完畢,重新從緩存中獲取數據。
2. 熱點數據設置隨機過期時間,后臺異步更新緩存,適用于不嚴格要求緩存一致性的場景。
  • 緩存穿透-緩存無數據/數據庫無數據

    緩存穿透(cache penetration)是用戶訪問的數據既不在緩存當中,也不在數據庫中。但出于容錯的考慮,如果從數據庫查詢不到數據,則無法寫入緩存。這就導致每次請求都會到底層數據庫進行查詢,緩存也失去了意義。當高并發或有人利用不存在的Key頻繁攻擊時,數據庫的壓力驟增,甚至崩潰,這就是緩存穿透問題。

在這里插入圖片描述

解決方案:
方案一:緩存空值(null)或默認值 + 過期時間在數據庫查詢不存在時,將其緩存為空值(null)或默認值,緩存失效時間一般設置為5分鐘之內,當數據庫被寫入或更新該key的新數據時,緩存必須同時被刷新,避免數據不一致。方案二:業務邏輯前置校驗在業務請求的入口處進行數據合法性校驗,檢查請求參數是否合理、是否包含非法值、是否惡意請求等,提前有效阻斷非法請求。比如,根據年齡查詢時,請求的年齡為-10歲,這顯然是不合法的請求參數,直接在參數校驗時進行判斷返回。方案三:使用布隆過濾器請求白名單寫入數據時,使用布隆過濾器進行標記(相當于設置白名單),業務請求發現緩存中無對應數據時,可先通過查詢布隆過濾器判斷數據是否在白名單內,如果不在白名單內,則直接返回空或失敗。方案四:用戶黑名單限制當發生異常情況時,實時監控訪問的對象和數據,分析用戶行為,針對故意請求、爬蟲或攻擊者,進行特定用戶的限制;

在這里插入圖片描述

  • 緩存雪崩-緩存無數據/數據庫有數據

    在使用緩存時,通常會對緩存設置過期時間,一方面目的是保持緩存與數據庫數據的一致性,另一方面是減少冷緩存占用過多的內存空間。但當緩存中大量熱點緩存在某一個時刻同時實效,請求全部轉發到數據庫,從而導致數據庫壓力驟增,造成系統崩潰等情況,這就是緩存雪崩(Cache Avalanche)。

在這里插入圖片描述

解決方案:
1. key均勻失效:   將key的過期時間后面加上一個隨機數(比如隨機1-5分鐘),讓key均勻的失效。
2. 雙key策略:		主key設置過期時間,備key不設置過期時間,當主key失效時,直接返回備key值。
3. 構建緩存高可用集群(針對緩存服務故障情況)

12. redis實現分布式session

基于redis的分布式session實現,依賴于前臺請求中攜帶的cookie和后臺生成的token。大致原理可以分為以下步驟:1,前端請求目標方法,攔截器判斷請求頭中是否攜帶cookie。
2,如果請求頭中攜帶cookie,則取出cookie并查詢redis中該cookie是否過期。如果沒有過期,則放行讓該請求去請求目標方法;如果已經過期,重新登陸
3,如果請求頭中,沒有攜帶cookie,則跳轉到登錄方法(同時攜帶當前請求的鏈接作為登錄后的回調地址)
4,進行登錄,登錄完畢生成指定的token存入redis中,生成cookie設置到response中。
5,登錄成功之后前端通過回調繼續請求目標方法。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/14477.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/14477.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/14477.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MQTT 5.0 報文解析 05:DISCONNECT

歡迎閱讀 MQTT 5.0 報文系列 的第五篇文章。在上一篇中&#xff0c;我們已經介紹了 MQTT 5.0 的 PINGREQ 和 PINGRESP 報文。現在&#xff0c;我們將介紹下一個控制報文&#xff1a;DISCONNECT。 在 MQTT 中&#xff0c;客戶端和服務端可以在斷開網絡連接前向對端發送一個 DIS…

手把手教你搭建一個花店小程序商城

如果你是一位花店店主&#xff0c;想要為你的生意搭建一個精美的小程序商城&#xff0c;以下是你將遵循的五個步驟。 步驟1&#xff1a;登錄喬拓云平臺進入后臺 首先&#xff0c;你需要登錄喬拓云平臺的后臺管理頁面。你可以在電腦或移動設備上的瀏覽器中輸入喬拓云的官方網站…

2024.5.26 機器學習周報

目錄 引言 Abstract 文獻閱讀 1、題目 2、引言 3、創新點 4、Motivation 5、naive Lite-HRNet 6、Lite-HRNet 7、實驗 深度學習 解讀SAM(Segment Anything Model) 1、SAM Task 2、SAM Model 2.1、Patch Embedding 2.2、Positiona Embedding 2.3、Transformer …

移動端適配:vw適配方案

vw (Viewport Width) 是一種長度單位&#xff0c;代表視口寬度的百分比。1vw 等于視口寬度的1%。在網頁設計和前端開發中&#xff0c;vw 單位常用于實現響應式設計和屏幕適配&#xff0c;尤其是針對不同尺寸和分辨率的移動設備。 為什么使用vw適配&#xff1f; 響應式: 使用v…

互聯網醫院開發:引領智慧醫療新時代

隨著科技的迅猛發展和互聯網的普及&#xff0c;傳統醫療模式正在迎來一場深刻的變革。互聯網醫院的崛起&#xff0c;打破了時間和空間的限制&#xff0c;為患者和醫療機構帶來了更加便捷、高效、安全的醫療服務體驗。本文將從技術角度深入探討互聯網醫院的開發&#xff0c;包括…

【openpcdet中yaml文件的DATA_AUGMENTOR學習】

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、代碼二、詳細解釋DISABLE_AUG_LISTAUG_CONFIG_LIST1. gt_sampling2. random_world_flip3. random_world_rotation4. random_world_scaling 總結 前言 提示…

多線程(八)

一、wait和notify 等待 通知 機制 和join的用途類似,多個線程之間隨機調度,引入 wait notify 就是為了能夠從應用層面上,干預到多個不同線程代碼的執行順序.( 這里說的干預,不是影響系統的線程調度策略 內核里的線程調度,仍然是無序的. 相當于是在應用程序…

Pod容器資源限制和探針

目錄 一、資源限制 1.Pod和容器的資源請求和限制 2.CPU 資源單位 案例一 案例二 二、健康檢查&#xff0c;又稱為探針&#xff08;Probe&#xff09; 1.探針的三種規則 2.Probe支持三種檢查方法 3.探測獲得的三種結果 案例一&#xff1a;exec 案例二&#xff1a;htt…

OneMO同行 心級服務:中移物聯OneMO模組助力客戶終端寒冷環境下的穩定運行

中移物聯OneMO模組以客戶為中心&#xff0c;基于中國移動心級服務要求&#xff0c;開展“OneMO同行 心級服務 標定一流”高標服務主題活動&#xff0c;升級“服務內容““服務方式”和“服務意識”&#xff0c;為行業客戶提供全新的服務體驗。 近日&#xff0c;某車載監控設備…

Hive語法學習總結

Hive SQL語法學習總結 hive參數庫操作1.創建庫2.具體案例3.庫的其他操作 表和庫的路徑演示表的操作創建表插入數據 hive參數 一 hive常用交互命令hive -e sql語句hive -f sql文件 //文件中是sql語句二 參數的設置方式一&#xff1a;在客戶端中設置參數(當次有效)set 參數名參…

ACM實訓第十七天

Is It A Tree? 問題 考試時應該做不出來&#xff0c;果斷放棄 樹是一種眾所周知的數據結構&#xff0c;它要么是空的(null, void, nothing)&#xff0c;要么是一個或的集合滿足以下屬性的節點之間有向邊連接的節點較多。 ?只有一個節點&#xff0c;稱為根節點&#xff0c;它…

【Crypto】摩絲

文章目錄 一、摩斯解題感悟 一、摩斯 很明顯莫爾斯密碼 iloveyou還挺浪漫 小小flag&#xff0c;拿下 解題感悟 莫爾斯密碼這種題還是比較明顯的

【董曉算法】競賽常用知識之圖論3(最近公共祖先)

前言&#xff1a; 本系列是學習了董曉老師所講的知識點做的筆記 董曉算法的個人空間-董曉算法個人主頁-嗶哩嗶哩視頻 (bilibili.com) 動態規劃系列&#xff08;還沒學完&#xff09; 【董曉算法】動態規劃之線性DP問題-CSDN博客 【董曉算法】動態規劃之背包DP問題&#xff…

智能鎖千千萬,誰是你的NO.1,親身實測凱迪仕傳奇大師K70旗艦新品

智能鎖千千萬&#xff0c;誰是你的NO.1。歡迎來到智哪兒評測室&#xff0c;這次我們為大家帶來了凱迪仕傳奇大師K70系列的一款重磅新品。 在科技的浪潮中&#xff0c;家居安全領域正經歷著前所未有的變革。智能鎖越來越成為家的安全守護神&#xff0c;以及智能生活的得力助手。…

Android 11 Framework實時監聽Activity堆棧變化

核心類 Framework中有一個類SystemActivityMonitoringService專門用于監控Activity堆棧變化&#xff0c;屬于隱藏Api&#xff0c;應用側無法調用。此類位于 packages/services/Car/service/src/com/android/car/SystemActivityMonitoringService.java 方法 void registerTa…

Mysql信息脫敏

類似微信姓名脫敏&#xff1a; SELECT CONCAT( REPEAT(*, CHAR_LENGTH(real_name) -1 ), RIGHT(real_name, 1) ) name from user_info電話號脫敏&#xff1a; SELECT CONCAT(LEFT(mobile_phone, 3), REPEAT(*, 4 ), RIGHT(mobile_phone, 4) ) phone from user_info

大數據Hive中的UDF:自定義數據處理的利器(下)

在上一篇文章中&#xff0c;我們對第一種用戶定義函數&#xff08;UDF&#xff09;進行了基礎介紹。接下來&#xff0c;本文將帶您深入了解剩余的兩種UDF函數類型。 文章目錄 1. UDAF1.1 簡單UDAF1.2 通用UDAF 2. UDTF3. 總結 1. UDAF 1.1 簡單UDAF 第一種方式是 Simple(簡單…

每日一題《leetcode--382.鏈表隨機結點》

https://leetcode.cn/problems/linked-list-random-node/ 這道題我們首先看到題目中的要求&#xff1a;在單鏈表中隨機選取一個鏈表中的結點&#xff0c;要使每個結點被選取的概率是一樣的。 當我們看到隨機這兩個字時&#xff0c;應該就會想起rand()這個函數。接著我們把使用這…

[暈事]今天做了件暈事35 VM發送給gateway太多ARP,導致攻擊檢查?

最近遇到一個問題&#xff0c;說網關學不到新起來VM的mac地址&#xff0c;通過tshark抓包發現&#xff0c;VM已經發出去GARP了。而且連續發送了24個GARP。 就認為是網關的問題&#xff0c;為什么沒網關沒有學到&#xff1f;就讓測試同事開網絡設備的ticket。 后來聽同事說&…

自己搭建內網穿透

本文介紹使用最新版frp搭建內網穿透&#xff0c;最新版本的frp在配置上與之前有很大不同&#xff0c;需要使用.toml文件進行配置。其中主要問題出現在toml文件內部。 一、云服務器配置 下載frp sudo apt update sudo apt install wget wget https://github.com/fatedier/frp…