最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Springboot如何優(yōu)雅高效的清除Redis中的業(yè)務(wù)key

 更新時(shí)間:2025年05月07日 08:54:50   作者:血小濺  
這篇文章主要為大家詳細(xì)介紹了Springboot如何優(yōu)雅高效的清除Redis中的業(yè)務(wù)key,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

1、問(wèn)題背景

云服務(wù)運(yùn)維工程師聯(lián)系我說(shuō)老系統(tǒng)有個(gè)服務(wù)連接redis集群實(shí)例使用keys命令導(dǎo)致實(shí)例夯住了并給我截了個(gè)圖。然后剛開(kāi)始我是挺懵逼的,同事跟我說(shuō)在某個(gè)服務(wù)中,我去找了找壓根沒(méi)有,后來(lái)我仔細(xì)想了想,并看了運(yùn)維老師提供的圖,我想到了方法找對(duì)應(yīng)的應(yīng)用進(jìn)程。以下是排查及解決過(guò)程。

2、如何找到對(duì)應(yīng)的應(yīng)用進(jìn)程

根據(jù)下面的圖,我們可以看到,redis集群的服務(wù)端口為9000,客戶端連接分配的客戶端本地通信端口【本地端口只是一個(gè)臨時(shí)標(biāo)識(shí),用于客戶端與 Redis 之間的通信,通常是由操作系統(tǒng)在每次創(chuàng)建新連接時(shí)自動(dòng)分配的,并不會(huì)影響連接的實(shí)際功能?!繛?9720,那么我們就可以通過(guò)netstat命令來(lái)查找對(duì)應(yīng)的應(yīng)用進(jìn)程了。

2.1、使用netstat查找進(jìn)程

進(jìn)入應(yīng)用部署的服務(wù)器,使用如下netstat命令查找進(jìn)程,如下圖,從下圖我們可以看出,進(jìn)程是個(gè)java進(jìn)程,進(jìn)程號(hào)為15817

netstat -anlp |grep 9000 |grep EST |grep 39720

2.2、使用jps命令查看應(yīng)用名稱

使用jps命令查看java進(jìn)程對(duì)應(yīng)的應(yīng)用名稱,通過(guò)命令我們可以看出

jps -l |grep 15817

3、問(wèn)題代碼及原因分析

3.1、查找問(wèn)題代碼

根據(jù)步驟2我們找到了對(duì)應(yīng)的應(yīng)用,下面我們就可以通過(guò)redis中的key關(guān)鍵詞YZ_MULTI_DIAG搜索代碼了,然后找到了如下圖的代碼,確實(shí)使用了keys命令。

private void cleanCache(String toUserId) {
    Set<String> keys = stringRedisTemplate.keys("YZ_MULTI_DIAG:" + toUserId + "*");
    stringRedisTemplate.delete(keys);
}

3.2、原因分析

keys 命令在 Redis 中遍歷所有的鍵,是一個(gè)阻塞操作,尤其是當(dāng) Redis 數(shù)據(jù)量大時(shí),可能會(huì)導(dǎo)致 Redis 實(shí)例卡住或響應(yīng)變慢。在 Redis 中,keys 命令用于查找與給定模式匹配的所有鍵,它會(huì)掃描整個(gè)數(shù)據(jù)庫(kù),并返回符合條件的所有鍵。這個(gè)命令在某些情況下會(huì)導(dǎo)致 Redis 實(shí)例“夯住”或變得非常緩慢,原因如下:

3.2.1、 阻塞和性能影響

  • keys 命令需要遍歷 Redis 實(shí)例中所有的鍵,無(wú)論數(shù)據(jù)庫(kù)中有多少個(gè)鍵。對(duì)于存儲(chǔ)大量鍵的 Redis 實(shí)例來(lái)說(shuō),keys 命令會(huì)消耗大量的 CPU 和內(nèi)存資源,因?yàn)樗仨殭z查每個(gè)鍵,并將結(jié)果返回給客戶端。
  • 如果有大量的鍵,keys 命令可能會(huì)導(dǎo)致 Redis 被阻塞,直到命令完成執(zhí)行。在此期間,Redis 無(wú)法處理其他客戶端請(qǐng)求,這可能會(huì)導(dǎo)致延遲或服務(wù)中斷。

3.2.2、 不適合生產(chǎn)環(huán)境

  • 在生產(chǎn)環(huán)境中,通常不建議使用 keys 命令,特別是在有大量鍵值對(duì)的情況下。keys 命令的性能是 O(N),其中 N 是數(shù)據(jù)庫(kù)中鍵的數(shù)量。這意味著數(shù)據(jù)庫(kù)中鍵越多,執(zhí)行時(shí)間就越長(zhǎng),負(fù)載越重。
  • 更適合使用 scan 命令,它是增量式的,并不會(huì)一次性返回所有匹配的鍵,而是通過(guò)多次迭代逐步獲取。這使得 Redis 在掃描鍵時(shí)不會(huì)被完全阻塞。

3.2.3、 其他客戶端請(qǐng)求的影響

  • 由于 keys 命令會(huì)導(dǎo)致 Redis 掃描整個(gè)鍵空間,它會(huì)占用 Redis 實(shí)例的 CPU 和內(nèi)存資源,這可能導(dǎo)致其他客戶端請(qǐng)求的響應(yīng)時(shí)間延遲,甚至阻塞其他操作,導(dǎo)致整個(gè) Redis 實(shí)例性能下降。
  • 在 Redis 集群環(huán)境中,keys 命令會(huì)對(duì)集群的每個(gè)節(jié)點(diǎn)進(jìn)行全局掃描,可能會(huì)對(duì)整個(gè)集群的性能產(chǎn)生影響。

4、優(yōu)化方案

  • 使用 scan 命令替代 keys 命令。scan 命令是增量的,可以分批次掃描鍵,避免一次性操作導(dǎo)致的阻塞。
  • 如果需要列出鍵,盡量使用特定的鍵模式(例如,前綴)來(lái)限制掃描的范圍,避免掃描整個(gè)數(shù)據(jù)庫(kù)。
  • 在生產(chǎn)環(huán)境中,應(yīng)該避免在高負(fù)載期間使用 keys 命令。

優(yōu)化后的代碼如下,使用類似分頁(yè)概念進(jìn)行批量刪除。

private void cleanCache(String toUserId) {
    String pattern = "YZ_MULTI_DIAG:" + toUserId + "*";
    ScanOptions scanOptions = ScanOptions.scanOptions().match(pattern).count(100).build();
    stringRedisTemplate.execute((RedisCallback<Void>) connection -> {
        String cursor = "0"; // 初始游標(biāo)
        try {
            do {
                // 使用SCAN命令分頁(yè)獲取匹配的鍵
                Cursor<byte[]> scanCursor = connection.scan(scanOptions);
                List<byte[]> keysToDelete = new ArrayList<>();
                while (scanCursor.hasNext()) {
                    keysToDelete.add(scanCursor.next());
                    // 分批刪除,避免內(nèi)存占用過(guò)高
                    if (keysToDelete.size() >= 100) {
                        connection.del(keysToDelete.toArray(new byte[0][]));
                        keysToDelete.clear();
                    }
                }
                // 刪除剩余的鍵
                if (!keysToDelete.isEmpty()) {
                    connection.del(keysToDelete.toArray(new byte[0][]));
                }
                cursor = scanCursor.getCursorId() + ""; // 更新游標(biāo)
            } while (!"0".equals(cursor)); // 如果游標(biāo)為0,表示掃描結(jié)束
        } catch (Exception e) {
            log.error("Error while scanning and deleting Redis keys with pattern: {}", pattern, e);
        }
        return null;
    });
}

5、測(cè)試驗(yàn)證

5.1、編寫(xiě)測(cè)試類

新增測(cè)試類,代碼如下,新增100個(gè)key,然后按照每個(gè)批次10個(gè)進(jìn)行刪除測(cè)試,代碼如下

package com.jianjang.zhgl.person.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.ActiveProfiles;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @program: zhgl_server
 * @description: 緩存清理測(cè)試類
 * @author: Jian Jang
 * @create: 2025-05-06 11:25:51
 * @blame ZHSF Team
 */

@Slf4j
@ActiveProfiles("local")
@SpringBootTest
public class RedisCleanCacheTest {
    /**
     * 測(cè)試key
     */
    private final static String TEST_KEY = "TEST_KEY:";
    private final static String BIZ_KEY = "userId";
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Test
    public void addCache() {
        for (int i = 0; i < 100; i++) {
            stringRedisTemplate.opsForValue().set(TEST_KEY+BIZ_KEY+i, "value" + i);
        }
    }
    @Test
    public void cleanCache() {
        cleanCache(BIZ_KEY, 10);
    }


    /**
     * 清除緩存內(nèi)容
     *
     * @param redisKey
     * @param batchSize
     */
    private void cleanCache(String redisKey, int batchSize) {
        String pattern = TEST_KEY + redisKey + "*";
        ScanOptions scanOptions = ScanOptions.scanOptions().match(pattern).count(batchSize).build();
        stringRedisTemplate.execute((RedisCallback<Void>) connection -> {
            String cursor = "0"; // 初始游標(biāo)
            try {
                do {
                    // 使用SCAN命令分頁(yè)獲取匹配的鍵
                    Cursor<byte[]> scanCursor = connection.scan(scanOptions);
                    List<byte[]> keysToDelete = new ArrayList<>();
                    while (scanCursor.hasNext()) {
                        keysToDelete.add(scanCursor.next());
                        // 分批刪除,避免內(nèi)存占用過(guò)高
                        if (keysToDelete.size() >= batchSize) {
                            connection.del(keysToDelete.toArray(new byte[0][]));
                            keysToDelete.clear();
                        }
                    }
                    // 刪除剩余的鍵
                    if (!keysToDelete.isEmpty()) {
                        connection.del(keysToDelete.toArray(new byte[0][]));
                    }
                    cursor = scanCursor.getCursorId() + ""; // 更新游標(biāo)
                } while (!"0".equals(cursor)); // 如果游標(biāo)為0,表示掃描結(jié)束
            } catch (Exception e) {
                log.error("Error while scanning and deleting Redis keys with pattern: {}", pattern, e);
            }
            return null;
        });
    }
}

5.2、測(cè)試新增

執(zhí)行新增測(cè)試方法后,新增成功,如下圖,

5.3、測(cè)試批量刪除

執(zhí)行批量刪除方法后,刪除成功,如下圖,100個(gè)TEST_KEY已被清除。

到此這篇關(guān)于Springboot如何優(yōu)雅高效的清除Redis中的業(yè)務(wù)key的文章就介紹到這了,更多相關(guān)Springboot清除Redis業(yè)務(wù)key內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot開(kāi)啟Swagger并配置基本信息方式

    SpringBoot開(kāi)啟Swagger并配置基本信息方式

    這篇文章主要介紹了SpringBoot開(kāi)啟Swagger并配置基本信息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • MySQL中的樂(lè)觀鎖和悲觀鎖入門(mén)簡(jiǎn)介

    MySQL中的樂(lè)觀鎖和悲觀鎖入門(mén)簡(jiǎn)介

    文章介紹了MySQL中的樂(lè)觀鎖和悲觀鎖兩種并發(fā)控制策略,悲觀鎖在每次訪問(wèn)數(shù)據(jù)時(shí)加鎖,適用于高沖突場(chǎng)景,樂(lè)觀鎖通過(guò)版本控制在更新時(shí)檢查數(shù)據(jù)是否被修改,適用于低沖突場(chǎng)景,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • java使用正則抓取網(wǎng)頁(yè)郵箱

    java使用正則抓取網(wǎng)頁(yè)郵箱

    這篇文章主要為大家詳細(xì)介紹了java使用正則抓取網(wǎng)頁(yè)郵箱的相關(guān)資料,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Spring Boot集成Spring Cloud Security進(jìn)行安全增強(qiáng)的方法

    Spring Boot集成Spring Cloud Security進(jìn)行安全增強(qiáng)的方法

    Spring Cloud Security是Spring Security的擴(kuò)展,它提供了對(duì)Spring Cloud體系中的服務(wù)認(rèn)證和授權(quán)的支持,包括OAuth2、JWT等,這篇文章主要介紹了Spring Boot集成Spring Cloud Security進(jìn)行安全增強(qiáng),需要的朋友可以參考下
    2024-11-11
  • 淺談foreach寫(xiě)失效的問(wèn)題

    淺談foreach寫(xiě)失效的問(wèn)題

    下面小編就為大家?guī)?lái)一篇淺談foreach寫(xiě)失效的問(wèn)題。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • Java Collections類操作集合詳解

    Java Collections類操作集合詳解

    這篇文章主要介紹了Java Collections類操作集合詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • springboot 增加過(guò)濾器方法操作示例

    springboot 增加過(guò)濾器方法操作示例

    這篇文章主要介紹了springboot 增加過(guò)濾器方法操作,結(jié)合實(shí)例形式分析了springboot過(guò)濾器配置、加載等相關(guān)操作技巧,需要的朋友可以參考下
    2019-12-12
  • Maven 生成打包可執(zhí)行jar包的方法步驟

    Maven 生成打包可執(zhí)行jar包的方法步驟

    這篇文章主要介紹了Maven 生成打包可執(zhí)行jar包的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • springmvc請(qǐng)求參數(shù)的綁定的項(xiàng)目實(shí)踐

    springmvc請(qǐng)求參數(shù)的綁定的項(xiàng)目實(shí)踐

    本文主要介紹了springmvc請(qǐng)求參數(shù)的綁定的項(xiàng)目實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11
  • RestTemplate接口調(diào)用神器常見(jiàn)用法匯總

    RestTemplate接口調(diào)用神器常見(jiàn)用法匯總

    這篇文章主要介紹了RestTemplate接口調(diào)用神器常見(jiàn)用法匯總,通過(guò)案例代碼詳細(xì)介紹RestTemplate接口調(diào)用神器常見(jiàn)用法,需要的朋友可以參考下
    2022-07-07

最新評(píng)論

永新县| 黄梅县| 屏东市| 遂川县| 呈贡县| 洪泽县| 海城市| 洛阳市| 长春市| 盐亭县| 宁夏| 莱芜市| 莱州市| 梅州市| 兴文县| 玉门市| 油尖旺区| 淮北市| 东丰县| 怀安县| 阿城市| 安塞县| 石家庄市| 河间市| 错那县| 乐亭县| 咸阳市| 遵义县| 成武县| 辽阳市| 临夏县| 安国市| 资源县| 嘉兴市| 和政县| 信丰县| 车险| 凤山县| 阳泉市| 冷水江市| 集贤县|