成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

給你1億的Redis key,如何高效統(tǒng)計(jì)?

數(shù)據(jù)庫 Redis
今天這篇文章就跟大家一起聊聊如果給你1億個Redis key,如何高效統(tǒng)計(jì)這個話題,希望對你會有所幫助。

前言

有些小伙伴在工作中,可能遇到過這樣的場景:老板突然要求統(tǒng)計(jì)Redis中所有key的數(shù)量,你隨手執(zhí)行了KEYS *命令,下一秒監(jiān)控告警瘋狂閃爍——整個Redis集群徹底卡死,線上服務(wù)大面積癱瘓。

今天這篇文章就跟大家一起聊聊如果給你1億個Redis key,如何高效統(tǒng)計(jì)這個話題,希望對你會有所幫助。

1.為什么不建議使用KEYS命令?

Redis的單線程模型是其高性能的核心,但也是最大的軟肋。

當(dāng)Redis執(zhí)行 KEYS * 命令時,內(nèi)部的流程如下:

圖片圖片

Redis的單線程模型是其高性能的核心,但同時也帶來一個關(guān)鍵限制:所有命令都是串行執(zhí)行的。

當(dāng)我們執(zhí)行 KEYS * 命令時:

Redis必須遍歷整個key空間(時間復(fù)雜度O(N))

在遍歷完成前,無法處理其他任何命令

對于1億個key,即使每個key查找只需0.1微秒,總耗時也高達(dá)10秒!

致命三連擊

  • 時間復(fù)雜度:1億key需要10秒+(實(shí)測單核CPU 0.1μs/key)
  • 內(nèi)存風(fēng)暴:返回結(jié)果太多可能撐爆客戶端內(nèi)存
  • 集群失效:在Cluster模式中只能查當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)。

如果Redis一次性返回的數(shù)據(jù)太多,可能會有OOM問題:

127.0.0.1:6379> KEYS *
(卡死10秒...)
(error) OOM command not allowed when used memory > 'maxmemory'

超過了最大內(nèi)存。

那么,Redis中有1億key,我們要如何統(tǒng)計(jì)數(shù)據(jù)呢?

2.SCAN命令

SCAN命令通過游標(biāo)分批遍歷,每次只返回少量key,避免阻塞。

Java版基礎(chǔ)SCAN的代碼如下:

public long safeCount(Jedis jedis) {
    long total = 0;
    String cursor = "0";
    ScanParams params = new ScanParams().count(500); // 每批500個
    
    do {
        ScanResult<String> rs = jedis.scan(cursor, params);
        cursor = rs.getCursor();
        total += rs.getResult().size();
    } while (!"0".equals(cursor)); // 游標(biāo)0表示結(jié)束
    
    return total;
}

使用游標(biāo)查詢Redis中的數(shù)據(jù),一次掃描500條數(shù)據(jù)。

但問題來了:1億key需要多久?

  • 每次SCAN耗時≈3ms
  • 每次返回500key
  • 總次數(shù)=1億/500=20萬次
  • 總耗時≈20萬×3ms=600秒=10分鐘!

3.多線程并發(fā)SCAN方案

現(xiàn)代服務(wù)器都是多核CPU,單線程掃描是資源浪費(fèi)。

看多線程優(yōu)化方案如下:

圖片圖片

多線程并發(fā)SCAN代碼如下:

public long parallelCount(JedisPool pool, int threads) throws Exception {
    ExecutorService executor = Executors.newFixedThreadPool(threads);
    AtomicLong total = new AtomicLong(0);
    
    // 生成初始游標(biāo)(實(shí)際需要更智能的分段)
    List<String> cursors = new ArrayList<>();
    for (int i = 0; i < threads; i++) {
        cursors.add(String.valueOf(i));
    }

    CountDownLatch latch = new CountDownLatch(threads);
    
    for (String cursor : cursors) {
        executor.execute(() -> {
            try (Jedis jedis = pool.getResource()) {
                String cur = cursor;
                do {
                    ScanResult<String> rs = jedis.scan(cur, new ScanParams().count(500));
                    cur = rs.getCursor();
                    total.addAndGet(rs.getResult().size());
                } while (!"0".equals(cur));
                latch.countDown();
            }
        });
    }
    
    latch.await();
    executor.shutdown();
    return total.get();
}

使用線程池、AtomicLong和CountDownLatch配合使用,實(shí)現(xiàn)了多線程掃描數(shù)據(jù),最終將結(jié)果合并。

性能對比(32核CPU/1億key):

方案

線程數(shù)

耗時

資源占用

單線程SCAN

1

580s

CPU 5%

多線程SCAN

32

18s

CPU 800%

4.分布式環(huán)境的分治策略

如果你的系統(tǒng)重使用了Redis Cluster集群模式,該模式會將數(shù)據(jù)分散在16384個槽(slot)中,統(tǒng)計(jì)就需要節(jié)點(diǎn)協(xié)同。

流程圖如下:

圖片圖片

每一個Redis Cluster集群中的master服務(wù)節(jié)點(diǎn),都負(fù)責(zé)統(tǒng)計(jì)一定范圍的槽(slot)中的數(shù)據(jù),最后將數(shù)據(jù)聚合起來返回。

集群版并行統(tǒng)計(jì)代碼如下:

public long clusterCount(JedisCluster cluster) {
    Map<String, JedisPool> nodes = cluster.getClusterNodes();
    AtomicLong total = new AtomicLong(0);
    
    nodes.values().parallelStream().forEach(pool -> {
        try (Jedis jedis = pool.getResource()) {
            // 跳過從節(jié)點(diǎn)
            if (jedis.info("replication").contains("role:slave")) return; 
            
            String cursor = "0";
            do {
                ScanResult<String> rs = jedis.scan(cursor, new ScanParams().count(500));
                total.addAndGet(rs.getResult().size());
                cursor = rs.getCursor();
            } while (!"0".equals(cursor));
        }
    });
    
    return total.get();
}

這里使用了parallelStream,會并發(fā)統(tǒng)計(jì)Redis不同的master節(jié)點(diǎn)中的數(shù)據(jù)。

5.毫秒統(tǒng)計(jì)方案

方案1:使用內(nèi)置計(jì)數(shù)器

如果只想統(tǒng)計(jì)一個數(shù)量,可以使用Redis內(nèi)置計(jì)數(shù)器,瞬時但非精確。

127.0.0.1:6379> info keyspace
# Keyspace
db0:keys=100000000,expires=20000,avg_ttl=3600

優(yōu)點(diǎn):毫秒級返回。

缺點(diǎn):包含已過期未刪除的key,法按模式過濾數(shù)據(jù)。

方案2:實(shí)時增量統(tǒng)計(jì)

實(shí)時增量統(tǒng)計(jì)方案精準(zhǔn)但復(fù)雜。

基于鍵空間通知的實(shí)時計(jì)數(shù)器,具體代碼如下:

@Configuration
publicclass KeyCounterConfig {
    
    @Bean
    public RedisMessageListenerContainer container(RedisConnectionFactory factory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        
        container.addMessageListener((message, pattern) -> {
            String event = new String(message.getBody());
            if(event.startsWith("__keyevent@0__:set")) {
                redisTemplate.opsForValue().increment("total_keys", 1);
            } elseif(event.startsWith("__keyevent@0__:del")) {
                redisTemplate.opsForValue().decrement("total_keys", 1);
            }
        }, new PatternTopic("__keyevent@*"));
        
        return container;
    }
}

使用監(jiān)聽器統(tǒng)計(jì)數(shù)量。

成本分析

  • 內(nèi)存開銷:額外存儲計(jì)數(shù)器
  • CPU開銷:增加5%-10%處理通知
  • 網(wǎng)絡(luò)開銷:集群模式下需跨節(jié)點(diǎn)同步

6.如何選擇方案?

本文中列舉出了多個統(tǒng)計(jì)Redis中key的方案,那么我們在實(shí)際工作中如何選擇呢?

下面用一張圖給大家列舉了選擇路線:

圖片圖片

各方案的時間和空間復(fù)雜度如下:

方案

時間復(fù)雜度

空間復(fù)雜度

精度

KEYS命令

O(n)

O(n)

精確

SCAN遍歷

O(n)

O(1)

精確

內(nèi)置計(jì)數(shù)器

O(1)

O(1)

不精確

增量統(tǒng)計(jì)

O(1)

O(1)

精確

硬件法則:

  • CPU密集型:多線程數(shù)=CPU核心數(shù)×1.5
  • IO密集型:線程數(shù)=CPU核心數(shù)×3
  • 內(nèi)存限制:控制批次大小(count參數(shù))

常見的業(yè)務(wù)場景:

  • 電商實(shí)時大屏:增量計(jì)數(shù)器+RedisTimeSeries
  • 離線數(shù)據(jù)分析:SCAN導(dǎo)出到Spark
  • 安全審計(jì):多節(jié)點(diǎn)并行SCAN

終極箴言:? 精確統(tǒng)計(jì)用分治? 實(shí)時查詢用增量? 趨勢分析用采樣? 暴力遍歷是自殺

真正的高手不是能解決難題的人,而是能預(yù)見并規(guī)避難題的人

在海量數(shù)據(jù)時代,選擇比努力更重要——理解數(shù)據(jù)本質(zhì),才能駕馭數(shù)據(jù)洪流。


責(zé)任編輯:武曉燕 來源: 蘇三說技術(shù)
相關(guān)推薦

2021-08-04 17:55:38

keysRedis數(shù)據(jù)庫

2025-02-21 08:20:33

2023-12-08 07:55:37

MySQL數(shù)據(jù)統(tǒng)計(jì)InnoDB

2015-10-23 10:39:21

2021-08-08 22:08:41

Redis開發(fā)網(wǎng)頁

2025-05-28 03:10:00

2020-03-31 17:05:39

Redis熱 key代理

2019-04-01 08:19:38

搜索系統(tǒng)美團(tuán)

2024-11-04 16:01:01

2020-07-29 09:54:35

帖子中心數(shù)據(jù)架構(gòu)

2021-09-13 07:46:06

Kubectl Kubernetes 工具

2025-01-14 16:14:10

2025-03-05 08:40:00

RedisJava開發(fā)

2021-05-24 08:58:34

Redis Bitmap 數(shù)據(jù)統(tǒng)計(jì)

2024-11-21 16:47:55

2021-04-15 11:37:47

NumpyPython代碼

2019-12-02 09:58:04

2024-06-04 15:53:12

Python數(shù)組

2024-05-29 12:47:27

2017-12-25 09:16:09

微信高效運(yùn)維
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 91在线观看视频 | 日韩精品在线一区二区 | 九九99精品| 亚洲欧美在线一区 | 亚洲欧美日韩成人在线 | 精品少妇一区二区三区在线播放 | 欧美日韩不卡合集视频 | 欧美a∨ | 91精品国产91久久久久游泳池 | 日本三级电影免费观看 | 日本三级全黄三级三级三级口周 | 精区3d动漫一品二品精区 | 欧美日在线 | 韩日一区 | 男人天堂社区 | 一区二区三区视频免费观看 | 久久久久久久久久久爱 | 97久久精品午夜一区二区 | 一二区视频 | 玖玖操| 亚洲一区二区三区在线播放 | 本地毛片| 免费三级av| 日韩一二区 | 久久久精品视频免费看 | 亚洲国产成人精品久久 | 成人99| 日本福利视频免费观看 | 欧美精品久久久 | 青春草91 | 美女国产 | 亚洲香蕉在线视频 | 中文字幕av在线播放 | h在线看| 欧美日韩电影免费观看 | 中文字幕日韩欧美 | 欧美视频二区 | 国产一区不卡 | 91在线观 | 51ⅴ精品国产91久久久久久 | 欧美日韩一 |