当前位置: 首页 > article >正文

Go Redis实现排行榜

文章目录

  • 第一章. 前言
    • 1.1. 排行榜的应用场景
    • 1.2. Redis在排行榜功能中的优势
  • 第二章. 排行榜系统的功能接口设计
    • 2.1. 接口设计的重要性
    • 2.2. 核心功能接口
      • 2.2.1. 排行榜系统总接口
      • 2.2.2. 排行榜数据源接口
      • 2.2.3. 排行榜打分器接口
      • 2.2.4. 排行榜数据存储接口
      • 2.2.5. 排行榜重建机制接口
  • 第三章. 排行榜功能接口的具体实现
    • 3.1. Redis客户端的初始化
    • 3.2. 核心接口的实现
      • 3.2.1. 总接口实现
      • 3.2.2. 数据存储实现
      • 3.2.3. 数据源获取实现
      • 3.2.4. 重建机制实现
      • 3.2.5. 打分器实现
  • 第四章. 排行榜功能的使用示例
    • 4.1. 具体使用场景
    • 4.2. 积分更新
    • 4.3. 获取排行榜数据
    • 4.4. 刷新数据到数据库
    • 4.5. 排行榜功能的扩展
  • 第五章. 深入分析与对比
    • 5.1. 使用 Redis 实现排行榜的优缺点
    • 5.2. Redis 实现与其他方式的对比
      • 5.2.1. 基于数据库的实现
      • 5.2.2. 内存排序算法实现
      • 5.2.3. 对比
  • 第六章. 总结与最佳实践
    • 6.1. 排行榜功能的核心要点
    • 6.2. 排行榜系统的设计原则
    • 6.3. 最佳实践清单
  • 附录


第一章. 前言

1.1. 排行榜的应用场景

排行榜是一种广泛应用于各类系统中的功能,能够直观展示数据的对比和竞争关系。以下是一些典型的应用场景:

1. 游戏排行
在游戏场景中,排行榜常被用来展示玩家的分数、等级或竞技表现。例如:

  • 单局排名:展示玩家在某一局游戏中的分数排名。
  • 全服排名:统计全服玩家的积分,显示排名前列的玩家。

2. 电商排行
电商平台利用排行榜来吸引用户关注热门商品和高口碑商家。常见的排行包括:

  • 销量排行榜:基于商品销量进行排名。
  • 好评率排行榜:按用户评价分数排序,突出优质商品。

3. 数据分析
在数据驱动的分析场景中,排行榜提供实时的数据对比,支持用户快速决策。例如:

  • 访问量排行:显示某一时间段内访问量最高的网页。
  • 点赞排行:按点赞数展示内容受欢迎程度。

1.2. Redis在排行榜功能中的优势

在实现排行榜的技术选型中,Redis以其高性能、高可用性成为广泛应用的工具。以下是它的几大优势:

1. 数据操作高效
Redis提供的有序集合(Sorted Set)数据结构,支持按照分数对元素排序,并提供高效的插入、查询操作。常用命令包括:

  • ZADD:向集合中添加成员并设置分数。
  • ZRANGE:按分数范围获取成员列表。
  • ZRANK:获取指定成员的排名。

2. 原子性操作
Redis的命令是原子性的,可以确保分数更新和排名获取等操作在高并发环境下的正确性。

3. 高可扩展性
Redis支持分布式部署,能够轻松处理大规模并发访问。此外,通过分片或数据归档,可以突破单节点内存限制。


第二章. 排行榜系统的功能接口设计

2.1. 接口设计的重要性

在构建复杂的系统如排行榜功能时,接口设计起到了规范功能模块的关键作用。良好的接口设计可以带来以下优势:

  1. 代码易读性:接口定义清晰,开发者可以快速理解功能。
  2. 扩展性:接口设计解耦了具体实现与业务逻辑,便于后续功能扩展。
  3. 维护性:通过接口隔离变更,可以在不影响外部调用者的情况下优化内部实现。

2.2. 核心功能接口

在排行榜系统中,我们将功能模块化,并为每个模块定义了对应的接口。

2.2.1. 排行榜系统总接口

总接口 RankSystemIFace 通过组合其他接口,定义了系统的整体功能:

type RankSystemIFace interface {
	RankSourceIFace
	RankScoreIFace
	RankStorageIFace
	RankRebuildIFace
}
  • 组合接口:将排行榜功能拆分为多个独立的功能模块,并通过组合实现总接口。
  • 便于扩展:未来若新增功能,只需添加新的接口,而无需修改已有代码。

2.2.2. 排行榜数据源接口

RankSourceIFace 用于管理排行榜的数据来源,例如从数据库或外部系统拉取数据。其方法定义如下:

type RankSourceIFace interface {
	// RankSourceItem 获取某个排行榜单项的数据
	RankSourceItem(ctx context.Context, rankListId RankListID, rankItemid RankItemID) (*RankZItem, error)
	// RankSourceRankList 获取排行榜列表
	RankSourceRankList(ctx context.Context, offset, limit int64) ([]RankListID, error)
	// RankSourceList 获取某个排行榜真实数据源
	RankSourceList(ctx context.Context, rankListId RankListID, offset, limit int64) ([]*RankZItem, error)
}
  • 方法解析
    • RankSourceItem:查询单个排行榜项。
    • RankSourceRankList:获取排行榜列表。
    • RankSourceList:获取某个排行榜真实数据源。

2.2.3. 排行榜打分器接口

RankScoreIFace 提供了分数的计算与反计算逻辑,支持灵活的分数管理:

type RankScoreIFace interface {
	// RankItemReScore 排行项重算分数
	RankItemReScore(ctx context.Context, item *RankZItem) (*RankZItem, error)
	// RankItemDeScore 排行项反算分数
	RankItemDeScore(ctx context.Context, item *RankZItem) (*RankZItem, error)
	// ReScore 重算分数
	ReScore(ctx context.Context, score int64, createTime int64) int64
	// DeScore 反算分数
	DeScore(ctx context.Context, encScore int64) (int64, int64)
}

  • 典型场景
    • 重算分数:玩家积分更新时重新计算分数。
    • 反算分数:排行榜中解码分数,便于显示原始信息。

2.2.4. 排行榜数据存储接口

RankStorageIFace 负责数据的增删改查,是排行榜功能的核心接口之一:

type RankStorageIFace interface {
	RankWriter
	RankReader
	ScoreRegister
}
  • 写操作接口
type RankWriter interface {
	StoreRankItem(ctx context.Context, rankListID RankListID, item *RankZItem, scope string) error // 单个存储
	BulkStoreRankItem(ctx context.Context, rankListID RankListID, items []*RankZItem, scope string) (int64, error) // 批量存储
	RemRankByItemId(ctx context.Context, rankListID RankListID, id RankItemID, scope string) error // 删除单个项
}
  • 读操作接口
type RankReader interface {
	GetRankItemById(ctx context.Context, rankListID RankListID, id RankItemID, scope string) (*RankZItem, error) // 查询单个成员
	RankList(ctx context.Context, rankListID RankListID, order RankOrder, offset, limit int64, scope string) ([]*RankZItem, error) // 查询排行榜
}
  • 注册打分器接口
type ScoreRegister interface {
	Register(Score RankScoreIFace) error
}

2.2.5. 排行榜重建机制接口

RankRebuildIFace 提供了数据的重建逻辑,用于修复或更新排行榜:

type RankRebuildIFace interface {
	Rebuild(ctx context.Context, RankListID RankListID) (int, error)
	RebuildAll(ctx context.Context) (int, error)
}
  • 典型场景:排行榜数据不一致或大规模更新时重建数据。

第三章. 排行榜功能接口的具体实现

在实现排行榜功能接口时,我们将重点放在如何利用 Redis 高效存储和操作数据,结合接口设计实现高度可扩展的功能模块。


3.1. Redis客户端的初始化

Redis 是排行榜系统的核心存储工具,其初始化需要考虑连接池配置和安全性。

初始化代码
以下代码展示了 Redis 客户端池的初始化:

redisPool := &redis.Pool{
    MaxIdle:     3,
    IdleTimeout: 240 * time.Second,
    Dial: func() (redis.Conn, error) {
        return redis.Dial("tcp", 
            "redis host", 
            redis.DialPassword("redis password"),
        )
    },
}
  • 连接池参数

    • MaxIdle:最大空闲连接数,确保在高并发情况下有足够的连接可用。
    • IdleTimeout:空闲连接的超时时间,避免长时间未使用的连接占用资源。
  • 安全性

    • 使用 redis.DialPassword 配置 Redis 密码,防止未授权访问。

3.2. 核心接口的实现

3.2.1. 总接口实现

RankSystem 是排行榜系统的总接口,其实现将所有子模块组合到一起:

type RankSystem struct {
    rank_kit.RankSourceIFace
    rank_kit.RankStorageIFace
    rank_kit.RankRebuildIFace
    rank_kit.RankScoreIFace
}
  • 组合接口:通过组合,RankSystem 封装了排行榜的所有核心功能,便于外部调用。

初始化代码

func NewRankSystem() (rank_kit.RankSystemIFace, error) {
    ranksource := &RankSource{}
    rankScorer := NewZSetRankScorer(DescTimeOrder)
    redisPool := &redis.Pool{/* 配置省略 */}
    storage := NewRedisStorage(redisPool, rankScorer)
    locker := NewLock()
    rankBuilder, err := NewRankRebuilder(ranksource, storage, 1, 4000, locker)
    if err != nil {
        return nil, err
    }
    return &RankSystem{
        RankSourceIFace:    ranksource,
        RankStorageIFace:   storage,
        RankRebuilderIFace: rankBuilder,
        RankScorerIFace:    rankScorer,
    }, nil
}

逻辑解析

  1. 初始化RankSource

  2. 初始化RankStorage

    • 初始化存储
    • 注入存储依赖
    • 注入打分器
  3. 初始化RankRebuild

    • 并发处理:利用 ants 工作池实现高效的并发处理,提升重建速度。
    • 错误处理:通过 multierr 累积所有发生的错误,确保重建过程中所有问题都能被记录。
    • 任务终止机制:一旦发生错误或读取到末尾,通过 finish 通道及时终止任务提交,避免不必要的资源浪费。
    • 结果汇总:通过专门的收集协程,安全地汇总处理数量和错误信息,确保数据一致性。
    • 锁机制:确保同一时间只有一个重建操作在进行,防止数据竞争和不一致问题。
  4. 初始化RankScore

    • 初始化打分器
    • 指定排序规则

3.2.2. 数据存储实现

数据存储是排行榜功能的核心模块,通过 Redis 的有序集合(Sorted Set)和哈希表实现高效的增删查改操作。

单个存储排行成员项
StoreRankItem 方法通过 ZADDHSET 存储数据:

func (r *RedisStorage) StoreRankItem(ctx context.Context, rankListID rank_kit.RankListID, item *rank_kit.RankZItem, scope string) error {
    conn, _ := r.redisPool.GetContext(ctx)
    defer conn.Close()

    // 重新计算分数
    newItem, err := r.RankScorer.RankItemReScore(ctx, item)
    if err != nil {
        return err
    }

    // 存储到 ZSet 和 Hash
    zk := r.zsetPrefix(rankListID, scope)
    zm := r.itemKey(string(newItem.RankItemID))
    ctxKey := r.hashPrefix(rankListID, scope)
    context, _ := json.Marshal(newItem.Context)

    _, err = conn.Do("ZADD", zk, newItem.Score, zm)
    if err != nil {
        return rank_kit.ErrRankStorageStoreRankItem
    }

    _, err = conn.Do("HSET", ctxKey, zm, context)
    return err
}
  • ZADD:存储排行榜项的分数和标识,支持按分数排序。
  • HSET:将上下文信息存储在哈希表中,用于查询详细数据。

查询排行榜
RankList 方法支持分页查询排行榜:

func (r *RedisStorage) RankList(ctx context.Context, rankListID rank_kit.RankListID, order rank_kit.RankOrder, offset int64, limit int64, scope string) ([]*rank_kit.RankZItem, error) {
    conn, _ := r.redisPool.GetContext(ctx)
    defer conn.Close()

    zk := r.zsetPrefix(rankListID, scope)
    var reply []string
    if order == rank_kit.AscRankOrder {
        reply, _ = redis.Strings(conn.Do("ZRANGE", zk, offset, offset+limit-1, "WITHSCORES"))
    } else {
        reply, _ = redis.Strings(conn.Do("ZREVRANGE", zk, offset, offset+limit-1, "WITHSCORES"))
    }

    return formatRankItemFromReplyStrings(reply)
}
  • 分页实现:通过 offsetlimit 参数实现结果分页,支持正序和倒序排序。

3.2.3. 数据源获取实现

RankSource 提供数据源接口的具体实现,支持从外部系统拉取排行榜数据。

func (f *RankSource) RankSourceList(ctx context.Context, rankListId rank_kit.RankListID, offset int64, limit int64) ([]*rank_kit.RankZItem, error) {
    // 示例实现:从数据库或其他存储获取数据
    return nil, nil
}

3.2.4. 重建机制实现

RankRebuild 模块实现排行榜数据的重建逻辑,支持高效批量处理。

重建单个排行榜

func (r *RankRebuild) Rebuild(ctx context.Context, RankListID rank_kit.RankListID) (int, error) {
  if locker, err := r.rebuildLock.Lock(ctx, string(RankListID)); err!= nil {
    return 0, err
  } else {
    defer func() {
      locker.Release(ctx)
    }()
  }

  // rebuild task start
  var result error
  var num int

  err := make(chan error)
  nums := make(chan int)
  done := make(chan struct{}, 1)
  finish := make(chan struct{}, 1)
  collectorFinish := make(chan struct{}, 1)

  wg := new(sync.WaitGroup)

  // 开启一个协程负责收集反馈的信息
  go func() {
    for {
      select {
      case <-done:
        close(collectorFinish)
        return
      case e := <-err:
        // 发生错误 终止任务
        result = multierr.Append(result, e)
        select {
        case <-finish:
        default:
          close(finish)
        }
      case n := <-nums:
        num = num + n
        // 如果收到0长度,证明已经循环到尾了
        if n == 0 {
          select {
          case <-finish:
          default:
            close(finish)
          }
        }
      }
    }
  }()

  // 循环提交读取数据的任务
ReadLoop:
  for i := 0; ; i++ {
    select {
    case <-finish:
      break ReadLoop
    default:
      wg.Add(1)
      offset := int64(i) * r.limit
      _ = r.pool.Invoke(newReadTask(ctx, RankListID, offset, r.limit, wg, nums, err, done))
    }
  }

  // 等待所有任务处理完成
  wg.Wait()
  close(done)
  <-collectorFinish

  return num, result
}

核心逻辑

  • 1.加锁
    • 锁定排行榜重建操作
  • 2.初始化变量和通道
    • result:用于累积所有可能发生的错误。
    • num:用于统计总处理的排行榜项数量。
    • err:用于接收各个任务中的错误。
    • nums:用于接收各个任务处理的数量。
    • done:用于通知任务终止。
    • finish:用于通知任务提交完成或发生错误需要终止。
    • collectorFinish:用于标识收集协程的完成。
  • 3.启动收集协程
    • 负责收集各个任务的错误和处理数量
    • 错误处理:一旦接收到错误,将其累积到 result 中,并触发 finish 通道以终止后续任务的提交。
    • 数量统计:累加每个任务处理的数量,如果接收到的数量为 0,说明已经处理完所有数据,触发finish通道。
    • 完成信号:当done通道被关闭时,关闭collectorFinish通道并结束协程。
  • 4.任务提交循环
    • 循环提交读取数据的任务,直到接收到finish信号。
  • 5.等待所有任务完成
    • 等待所有提交的任务完成,并确保收集协程也已结束。
  • 6.返回结果
    • num:总共处理的排行榜项数量。
    • result:可能发生的所有错误,使用multierr进行累积。

3.2.5. 打分器实现

打分器 ZSetRankScore 实现了复杂的分数计算规则,支持时间和分数的综合排序。

分数编码规则

func (z *ZSetRankScore) RankItemReScore(ctx context.Context, item *rank_kit2.RankZItem) (*rank_kit2.RankZItem, error) {

	tmp := *(item)

	tmp.Score = genScore(item.Score, genTimeScore(z.TimeOrder, int64(item.Time)))

	return &tmp, nil
}

func (z *ZSetRankScore) RankItemDeScore(ctx context.Context, item *rank_kit2.RankZItem) (*rank_kit2.RankZItem, error) {

	tmp := *(item)
	score := tmp.Score
	tmp.Score = deScore(score)
	tmp.Time = deCreateTime(score, z.TimeOrder)

	return &tmp, nil
}

func (z *ZSetRankScore) ReScore(ctx context.Context, score int64, createTime int64) int64 {
	return genScore(score, createTime)
}

func (z *ZSetRankScore) DeScore(ctx context.Context, score int64) (int64, int64) {
	return deScore(score), deCreateTime(score, z.TimeOrder)
}

核心逻辑

    1. 首位标志位不用,高31位存储分数,低32位存储时间;
    1. 如果时间倒序,则直接存储时间;
    1. 如果时间正序,则直接MAX_TIME-时间。

第四章. 排行榜功能的使用示例

在实现了排行榜的核心功能后,接下来展示如何在实际场景中调用这些功能。通过应用场景示例,说明如何使用排行榜系统进行积分更新、排行榜查询和数据管理。


4.1. 具体使用场景

本章以积分排行榜为例,演示如何通过 RankSystem 提供的功能接口,实现以下需求:

  1. 积分更新:用户的积分变动时更新排行榜。
  2. 排行榜查询:按月获取排行榜数据,包括当前用户的排名。
  3. 数据管理:定期刷新排行榜数据到数据库。

4.2. 积分更新

IntegralChangeHandle 方法处理用户积分的变更,将其写入 Redis 中的排行榜。

代码实现

type IntegralRankService struct {
  RankSystem rank_kit.RankSystemIFace
}

var singleGroup *singleflight.Group

func NewIntegralRankService() *IntegralRankService {
  integralRankSystem, _ := NewRankSystem()
  return &IntegralRankService{
    RankSystem: integralRankSystem,
  }
}

const MAX_RANK = 100

func (s *IntegralRankService) IntegralChangeHandle(ctx context.Context, opt *IntegralChange) error {
  updateTime := time.Unix(opt.UpdateTime, 0)

  yearMonth := getYearMonth(uint32(updateTime.Year()), uint32(updateTime.Month()))

  item, err := s.RankSystem.GetRankItemById(ctx, rank_kit.RankListID(strconv.Itoa(int(opt.ListId))), rank_kit.RankItemID(strconv.Itoa(int(opt.UserId))), yearMonth)
  if err!= nil {
    // 未找到排行榜
    if err == rank_kit.ErrRankStorageNotFoundItem {
      //从db 获取数据,重建
      item = &rank_kit.RankZItem{
        ItemContext: rank_kit.ItemContext{
          RankItemID: rank_kit.RankItemID(strconv.Itoa(int(opt.UserId))),
          Context: map[string]string{
            "业务key": "业务数据",
          },
        },
        Score: 0,
        Time:  time.Now().Unix(),
      }
    } else {
      return err
    }
  }

  item.Score = item.Score + opt.Integral

  // 写入当前配置
  if err = s.RankSystem.StoreRankItem(ctx, rank_kit.RankListID(strconv.Itoa(int(opt.ListId))), item, yearMonth); err!= nil {
    return err
  }

  return nil
}

逻辑解析

  1. 获取用户积分项

    • 调用 GetRankItemById 查询用户当前的积分信息。
    • 若未找到对应记录,则初始化用户积分项。
  2. 更新积分

    • 累加积分变动值到 item.Score
  3. 写入 Redis

    • 使用 StoreRankItem 更新排行榜。

4.3. 获取排行榜数据

IntegralRankMonthList 方法获取指定月份的积分排行榜,并返回当前用户的排名信息。

代码实现

func (s *IntegralRankService) IntegralRankMonthList(ctx context.Context, req *IntegralRankListRequest) (*IntegralRankListResponse, error) {
    yearMonth := getYearMonth(req.Year, req.Month)
    var RankSystem rank_kit.RankSystemIFace

    // 获取排行榜
    items, err := RankSystem.RankList(ctx, 
        rank_kit.RankListID(strconv.Itoa(int(req.IncentiveSchemeId))),
        rank_kit.DescRankOrder,
        0,
        MAX_RANK,
        yearMonth,
    )
    if err != nil {
        return nil, err
    }

    // 构建排行榜返回值
    var list []*RankItem
    for rank, item := range items {
        userId, _ := strconv.Atoi(string(item.RankItemID))
        list = append(list, &RankItem{
            UserId:             int64(userId),
            AddIntegralByMonth: item.Score,
            Rank:               uint32(rank + 1),
            Status:             1,
        })
    }

    // 查询当前用户排名
    self := &RankItem{UserId: int64(req.UserId)}
    if selfItem, err := RankSystem.GetRankItemById(ctx,
        rank_kit.RankListID(strconv.Itoa(int(req.IncentiveSchemeId))),
        rank_kit.RankItemID(strconv.Itoa(int(req.UserId))),
        yearMonth,
    ); err == nil {
        self.AddIntegralByMonth = selfItem.Score
        self.Status = 1
    }

    return &IntegralRankListResponse{
        Self:              self,
        List:              list,
        IncentiveSchemeId: req.IncentiveSchemeId,
        UpdatedTime:       time.Now().Unix(),
    }, nil
}

逻辑解析

  1. 获取排行榜数据

    • 通过 RankList 查询 Redis 中的排行榜数据。
  2. 构建返回数据

    • 根据 Redis 返回的用户数据,构造每个用户的排行榜项(RankItem)。
  3. 获取当前用户排名

    • 调用 GetRankItemById 查询当前用户在排行榜中的位置及积分。

4.4. 刷新数据到数据库

定期将排行榜数据写入数据库,便于后续数据分析和持久化存储。

代码实现

func (s *IntegralRankService) Flush(ctx context.Context, yearMonth string, force bool, incentiveSchemeID uint64) error {
    var RankSystem rank_kit.RankSystemIFace

    if force {
        _, err := RankSystem.Rebuild(ctx, rank_kit.RankListID(strconv.Itoa(int(incentiveSchemeID))))
        if err != nil {
            return err
        }
    }

    // 获取 Redis 数据
    items, err := RankSystem.RankList(ctx, 
        rank_kit.RankListID(strconv.Itoa(int(incentiveSchemeID))),
        rank_kit.DescRankOrder,
        0,
        MAX_RANK,
        yearMonth,
    )
    if err != nil {
        return err
    }

    // 写入数据库
    for _, item := range items {
        integralRank, _ := IntegralRankFromZItem(yearMonth, incentiveSchemeID, item)
        fmt.Println("写入数据库:", integralRank)
        // 执行数据库写入逻辑
    }

    return nil
}

逻辑解析

  1. 强制刷新排行榜

    • 调用 Rebuild 强制重建 Redis 中的排行榜数据。
  2. 获取 Redis 数据

    • 使用 RankList 查询 Redis 中存储的排行榜数据。
  3. 写入数据库

    • 将 Redis 中的排行榜数据逐条持久化到数据库。

4.5. 排行榜功能的扩展

支持多维度排行榜
通过 SetScope 方法,可以在不同的时间范围或分类下管理排行榜,例如:

  • 每日排行榜。
  • 每周排行榜。

数据归档与清理

  • 定期将 Redis 数据写入数据库后,删除 Redis 中的历史数据,释放内存。

第五章. 深入分析与对比

在实现了基于 Redis 的排行榜功能后,我们需要深入分析其优缺点,并与其他实现方式进行对比,以便更好地选择和优化。


5.1. 使用 Redis 实现排行榜的优缺点

优势

  1. 性能高效

    • Redis 的有序集合(Sorted Set)能够以 O(logN) 的时间复杂度完成插入、删除、查询操作。
    • 支持大规模并发访问,非常适合实时性要求高的排行榜场景。
  2. 操作便捷

    • 通过简单的命令(如 ZADDZRANGE),即可完成复杂的排序、分页查询。
  3. 高可用性与扩展性

    • Redis 支持主从复制和分片存储,能够在高并发场景下保持稳定性能。

局限

  1. 内存瓶颈

    • Redis 是内存型数据库,当数据量过大时,内存占用将成为瓶颈。
  2. 数据持久化成本

    • Redis 的快照(RDB)和日志(AOF)功能虽然提供了持久化支持,但会增加系统的复杂性和存储开销。
  3. 单点存储限制

    • 即使使用分片存储,单个 Redis 实例的存储能力仍有限制,需要仔细规划数据分布。

5.2. Redis 实现与其他方式的对比

5.2.1. 基于数据库的实现

  • 特点
    • 使用关系型数据库(如 MySQL)存储排行榜,依赖 SQL 查询实现排名计算。
  • 优点
    • 数据持久化能力强,适合长时间存储。
  • 缺点
    • SQL 排序操作效率较低,尤其在高并发场景中性能不佳。

5.2.2. 内存排序算法实现

  • 特点
    • 在应用层使用内存排序算法(如快速排序、堆排序)实时更新排行榜。
  • 优点
    • 不依赖外部存储,处理小规模数据时效率极高。
  • 缺点
    • 内存占用大,且无法持久化数据。

5.2.3. 对比

实现方式优势劣势
Redis高性能、高并发支持,实时性强内存消耗大,持久化复杂
数据库数据安全性强,适合长期存储性能不适合实时高并发场景
内存排序算法快速、高效,适合小规模数据数据无法持久化,不适合大规模排行榜场景

第六章. 总结与最佳实践

6.1. 排行榜功能的核心要点

  1. 数据结构选择

    • Redis 的有序集合是高效实现排行榜的核心,支持按分数排序、范围查询等操作。
  2. 系统解耦

    • 通过模块化接口设计,实现业务逻辑与数据存储的分离,便于扩展和维护。
  3. 实时性与持久化的权衡

    • 利用 Redis 提供的实时性能,并结合数据库完成持久化和归档。

6.2. 排行榜系统的设计原则

  1. 易维护性

    • 接口清晰,模块职责分明,便于开发和排查问题。
  2. 高性能

    • 减少 Redis 操作次数,采用批量操作和延迟加载等优化策略。
  3. 可扩展性

    • 支持多维度、多场景排行榜,灵活适应业务需求。

6.3. 最佳实践清单

  1. 定期数据清理

    • 定期将 Redis 数据归档到数据库,清理过期数据释放内存。
  2. 优化分数更新逻辑

    • 合并频繁的分数变更,减少 Redis 操作次数。
  3. 合理设计存储 Key

    • 根据业务场景设计有序集合的 Key,例如按时间范围或分类进行分组存储。
  4. 分布式扩展

    • 对于大规模排行榜,可使用 Redis Cluster 或分片技术进行水平扩展。

附录

附录A:完整代码示例
go-rank

附录B:常见问题与解答

  • :如何避免 Redis 内存占用过高?
    :定期归档数据到数据库,并设置过期时间清理历史数据。

  • :排行榜如何支持多维度?
    :通过 Scope 或 Key 分组,存储不同维度的排行榜数据。

附录C:Redis 常用命令参考

命令功能示例
ZADD添加元素并设置分数ZADD key score member
ZRANGE获取指定范围内的成员(正序)ZRANGE key start stop
ZREVRANGE获取指定范围内的成员(倒序)ZREVRANGE key start stop
ZSCORE获取成员的分数ZSCORE key member


http://www.kler.cn/a/455678.html

相关文章:

  • 其它结构型模式
  • Springboot关于格式化记录
  • 定义Shape:打造属于你的独特图形
  • AEO海关认证的注意事项
  • 闲谭Scala(1)--简介
  • moviepy将图片序列制作成视频并加载字幕 - python 实现
  • 神经网络-ResNet
  • 【社区投稿】自动特征auto trait的扩散规则
  • Effective C++ 条款32:确定你的 public 继承塑模出 is-a 关系
  • Pytorch | 利用IE-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击
  • 如何在 Spring Boot 微服务中设置和管理多个数据库
  • 对外发PDF设置打开次数
  • Python机器学习笔记(十五、聚类算法的对比和评估)
  • 【JavaEE进阶】@RequestMapping注解
  • 《一文读懂BP神经网络:从原理到应用》
  • redis中,msyql数据库读写分离搭建
  • Gitlab17.7+Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用)
  • C语言-结构体嵌套
  • 服务器数据恢复—Lustre分布式文件系统下服务器节点进水的数据恢复案例
  • 【网络工程师教程】六、网络互联与互联网
  • github codespaces推送镜像时unauthorized: access token has insufficient scopes 解决方法
  • 云服务器yum无法解析mirrorlist.centos.org
  • 网安瞭望台第17期:Rockstar 2FA 故障催生 FlowerStorm 钓鱼即服务扩张现象剖析
  • oracle基础:理解 Oracle SQL 中的 WHERE 后的 (+) 用法
  • 在k8s上使用strimzi operator安装kafka集群
  • selenium学习笔记(二)