自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

排行榜的五種方案!

數(shù)據(jù)庫(kù) 其他數(shù)據(jù)庫(kù)
今天我想和大家分享 6 種不同的排行榜實(shí)現(xiàn)方案,從簡(jiǎn)單到復(fù)雜,從單機(jī)到分布式,希望能幫助大家在實(shí)際工作中做出更合適的選擇。

引言

在工作的這些年中,我見(jiàn)證過(guò)太多團(tuán)隊(duì)在實(shí)現(xiàn)排行榜功能時(shí)踩過(guò)的坑。

今天我想和大家分享 6 種不同的排行榜實(shí)現(xiàn)方案,從簡(jiǎn)單到復(fù)雜,從單機(jī)到分布式,希望能幫助大家在實(shí)際工作中做出更合適的選擇。

有些小伙伴在工作中可能會(huì)覺(jué)得:不就是個(gè)排行榜嗎?搞個(gè)數(shù)據(jù)庫(kù)排序不就完了?

但實(shí)際情況遠(yuǎn)比這復(fù)雜得多。

當(dāng)數(shù)據(jù)量達(dá)到百萬(wàn)級(jí)、千萬(wàn)級(jí)時(shí),簡(jiǎn)單的數(shù)據(jù)庫(kù)查詢(xún)可能就會(huì)成為系統(tǒng)的瓶頸。

接下來(lái),我將為大家詳細(xì)剖析 6 種不同的實(shí)現(xiàn)方案,希望對(duì)你會(huì)有所幫助。

方案一:數(shù)據(jù)庫(kù)直接排序

適用場(chǎng)景:數(shù)據(jù)量小(萬(wàn)級(jí)以下),實(shí)時(shí)性要求不高

這是最簡(jiǎn)單直接的方案,幾乎每個(gè)開(kāi)發(fā)者最先想到的方法。

示例代碼如下:

public List<UserScore> getRankingList() {
    String sql = "SELECT user_id, score FROM user_scores ORDER BY score DESC LIMIT 100";
    return jdbcTemplate.query(sql, new UserScoreRowMapper());
}

優(yōu)點(diǎn)

  • 實(shí)現(xiàn)簡(jiǎn)單
  • 代碼維護(hù)成本低
  • 適合數(shù)據(jù)量小的場(chǎng)景

缺點(diǎn)

  • 數(shù)據(jù)量大時(shí)性能急劇下降
  • 每次查詢(xún)都需要全表掃描
  • 高并發(fā)下數(shù)據(jù)庫(kù)壓力大

架構(gòu)圖如下

圖片圖片

方案二:緩存+定時(shí)任務(wù)

適用場(chǎng)景:數(shù)據(jù)量中等(十萬(wàn)級(jí)),可以接受分鐘級(jí)延遲

這個(gè)方案在方案一的基礎(chǔ)上引入了緩存機(jī)制。

示例代碼如下:

@Scheduled(fixedRate = 60000) // 每分鐘執(zhí)行一次
public void updateRankingCache() {
    List<UserScore> rankings = userScoreDao.getTop1000Scores();
    redisTemplate.opsForValue().set("ranking_list", rankings);
}

public List<UserScore> getRankingList() {
    return (List<UserScore>) redisTemplate.opsForValue().get("ranking_list");
}

優(yōu)點(diǎn)

  • 減輕數(shù)據(jù)庫(kù)壓力
  • 查詢(xún)速度快(O(1))
  • 實(shí)現(xiàn)相對(duì)簡(jiǎn)單

缺點(diǎn)

  • 數(shù)據(jù)有延遲(取決于定時(shí)任務(wù)頻率)
  • 內(nèi)存占用較高
  • 排行榜更新不及時(shí)

架構(gòu)圖如下

圖片圖片

方案三:Redis有序集合

適用場(chǎng)景:數(shù)據(jù)量大(百萬(wàn)級(jí)),需要實(shí)時(shí)更新

Redis的有序集合(Sorted Set)是實(shí)現(xiàn)排行榜的利器。

示例代碼如下:

public void addUserScore(String userId, double score) {
    redisTemplate.opsForZSet().add("ranking", userId, score);
}

public List<String> getTopUsers(int topN) {
    return redisTemplate.opsForZSet().reverseRange("ranking", 0, topN - 1);
}

public Long getUserRank(String userId) {
    return redisTemplate.opsForZSet().reverseRank("ranking", userId) + 1;
}

優(yōu)點(diǎn)

  • 高性能(O(log(N))時(shí)間復(fù)雜度)
  • 支持實(shí)時(shí)更新
  • 天然支持分頁(yè)
  • 可以獲取用戶(hù)排名

缺點(diǎn)

  • 單機(jī)Redis內(nèi)存有限
  • 需要考慮Redis持久化
  • 分布式環(huán)境下需要額外處理

架構(gòu)圖如下

圖片圖片

方案四:分片+Redis集群

適用場(chǎng)景:超大規(guī)模數(shù)據(jù)(千萬(wàn)級(jí)以上),高并發(fā)場(chǎng)景

當(dāng)單機(jī)Redis無(wú)法滿(mǎn)足需求時(shí),可以采用分片方案。

示例代碼如下:

// 
public void addUserScore(String userId, double score) {
    RScoredSortedSet<String> set = redisson.getScoredSortedSet("ranking:" + getShard(userId));
    set.add(score, userId);
}

private String getShard(String userId) {
    // 簡(jiǎn)單哈希分片
    int shard = Math.abs(userId.hashCode()) % 16;
    return "shard_" + shard;
}

在這里我們以Redisson客戶(hù)端為例。

優(yōu)點(diǎn)

  • 水平擴(kuò)展能力強(qiáng)
  • 可以支持超大規(guī)模數(shù)據(jù)
  • 高并發(fā)下性能穩(wěn)定

缺點(diǎn)

  • 架構(gòu)復(fù)雜度高
  • 跨分片查詢(xún)困難
  • 需要維護(hù)分片策略

架構(gòu)圖如下

圖片圖片

方案五:預(yù)計(jì)算+分層緩存

適用場(chǎng)景:排行榜更新不頻繁,但訪(fǎng)問(wèn)量極大

這種方案結(jié)合了預(yù)計(jì)算和多級(jí)緩存。

示例代碼如下:

@Scheduled(cron = "0 0 * * * ?") // 每小時(shí)計(jì)算一次
public void precomputeRanking() {
    Map<String, Integer> rankings = calculateRankings();
    redisTemplate.opsForHash().putAll("ranking:hourly", rankings);
    
    // 同步到本地緩存
    localCache.putAll(rankings);
}

public Integer getUserRank(String userId) {
    // 1. 先查本地緩存
    Integer rank = localCache.get(userId);
    if (rank != null) return rank;
    
    // 2. 再查Redis
    rank = (Integer) redisTemplate.opsForHash().get("ranking:hourly", userId);
    if (rank != null) {
        localCache.put(userId, rank); // 回填本地緩存
        return rank;
    }
    
    // 3. 最后查DB
    return userScoreDao.getUserRank(userId);
}

優(yōu)點(diǎn)

  • 訪(fǎng)問(wèn)性能極高(本地緩存O(1))
  • 減輕Redis壓力
  • 適合讀多寫(xiě)少場(chǎng)景

缺點(diǎn)

  • 數(shù)據(jù)實(shí)時(shí)性差
  • 預(yù)計(jì)算資源消耗大
  • 實(shí)現(xiàn)復(fù)雜度高

架構(gòu)圖如下

圖片圖片

方案六:實(shí)時(shí)計(jì)算+流處理

適用場(chǎng)景:需要實(shí)時(shí)更新且數(shù)據(jù)量極大的社交平臺(tái)

這種方案采用流處理技術(shù)實(shí)現(xiàn)實(shí)時(shí)排行榜。

使用Apache Flink示例如下:

DataStream<UserAction> actions = env.addSource(new UserActionSource());

DataStream<Tuple2<String, Double>> scores = actions
    .keyBy(UserAction::getUserId)
    .process(new ProcessFunction<UserAction, Tuple2<String, Double>>() {
        private MapState<String, Double> userScores;
        
        public void open(Configuration parameters) {
            MapStateDescriptor<String, Double> descriptor = 
                new MapStateDescriptor<>("userScores", String.class, Double.class);
            userScores = getRuntimeContext().getMapState(descriptor);
        }
        
        public void processElement(UserAction action, Context ctx, Collector<Tuple2<String, Double>> out) {
            double newScore = userScores.getOrDefault(action.getUserId(), 0.0) + calculateScore(action);
            userScores.put(action.getUserId(), newScore);
            out.collect(new Tuple2<>(action.getUserId(), newScore));
        }
    });

scores.keyBy(0)
      .process(new RankProcessFunction())
      .addSink(new RankingSink());

優(yōu)點(diǎn)

  • 真正的實(shí)時(shí)更新
  • 可處理超高并發(fā)
  • 支持復(fù)雜計(jì)算邏輯

缺點(diǎn)

  • 架構(gòu)復(fù)雜度高
  • 運(yùn)維成本高
  • 需要專(zhuān)業(yè)團(tuán)隊(duì)維護(hù)

架構(gòu)圖如下

圖片圖片

方案對(duì)比與選擇

方案

數(shù)據(jù)量

實(shí)時(shí)性

復(fù)雜度

適用場(chǎng)景

數(shù)據(jù)庫(kù)排序

個(gè)人項(xiàng)目、小規(guī)模應(yīng)用

緩存+定時(shí)任務(wù)

中小型應(yīng)用,可接受延遲

Redis有序集合

大型應(yīng)用,需要實(shí)時(shí)更新

分片+Redis集群

超大

超大型應(yīng)用,超高并發(fā)

預(yù)計(jì)算+分層緩存

中高

讀多寫(xiě)少,訪(fǎng)問(wèn)量極大

實(shí)時(shí)計(jì)算+流處理

超大

實(shí)時(shí)

極高

社交平臺(tái),需要實(shí)時(shí)排名

總結(jié)

在選擇排行榜實(shí)現(xiàn)方案時(shí),我們需要綜合考慮以下幾個(gè)因素:

  1. 數(shù)據(jù)規(guī)模:數(shù)據(jù)量大小直接決定了我們選擇哪種方案
  2. 實(shí)時(shí)性要求:是否需要秒級(jí)更新,還是分鐘級(jí)甚至小時(shí)級(jí)都可以接受
  3. 并發(fā)量:系統(tǒng)的預(yù)期訪(fǎng)問(wèn)量是多少
  4. 開(kāi)發(fā)資源:團(tuán)隊(duì)是否有足夠的技術(shù)能力維護(hù)復(fù)雜方案
  5. 業(yè)務(wù)需求:排行榜的計(jì)算邏輯是否復(fù)雜

對(duì)于大多數(shù)中小型應(yīng)用,方案二(緩存+定時(shí)任務(wù))或方案三(Redis有序集合)已經(jīng)足夠。如

果業(yè)務(wù)增長(zhǎng)迅速,可以逐步演進(jìn)到方案四(分片+Redis集群)。

而對(duì)于社交平臺(tái)等需要實(shí)時(shí)更新的場(chǎng)景,則需要考慮方案五(預(yù)計(jì)算+分層緩存)或方案六(實(shí)時(shí)計(jì)算+流處理),但要做好技術(shù)儲(chǔ)備和架構(gòu)設(shè)計(jì)。

最后,無(wú)論選擇哪種方案,都要做好監(jiān)控和性能測(cè)試。排行榜作為高頻訪(fǎng)問(wèn)的功能,其性能直接影響用戶(hù)體驗(yàn)。

建議在實(shí)際環(huán)境中進(jìn)行壓測(cè),根據(jù)測(cè)試結(jié)果調(diào)整方案。

希望這六種方案的詳細(xì)解析能幫助大家在工作中做出更合適的選擇。


記住,沒(méi)有最好的方案,只有最適合的方案。

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

2013-08-23 09:41:19

2014-07-30 12:56:56

2025-03-10 12:10:00

RedisJava排行榜

2022-06-17 12:10:07

RPA機(jī)器人流程自動(dòng)化

2017-12-12 10:26:55

醫(yī)療 解決

2013-09-27 11:32:29

編程語(yǔ)言

2024-05-15 17:21:18

RedisSpring數(shù)據(jù)

2019-10-21 10:59:52

編程語(yǔ)言JavaC

2020-03-07 22:01:58

編程語(yǔ)言JavaPython

2022-06-08 13:50:41

AI專(zhuān)業(yè)排行

2022-08-09 08:29:50

TIOBE編程語(yǔ)言排行榜程序員

2013-06-28 15:57:55

蘋(píng)果App Store

2025-01-02 13:07:24

2022-01-06 22:54:08

編程語(yǔ)言CPython

2023-06-09 15:39:40

編程語(yǔ)言Python

2018-02-08 09:19:34

linux

2019-07-23 14:14:59

編程語(yǔ)言JavaPython

2020-08-13 11:55:33

編程語(yǔ)言JavaPython

2020-02-14 09:19:12

編程語(yǔ)言JavaPython

2013-04-01 09:50:15

Web框架Web
點(diǎn)贊
收藏

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