Redis®*: 实时排名用户

学习如何使用Redis实时为200万用户按分数排名

👋 欢迎来到 Stackhero 文档!

Stackhero 提供现成的 Redis 云 解决方案,具有众多优势,包括:

  • 包含 Redis Commander 网页界面
  • 无限消息大小和传输。
  • 只需点击即可轻松更新
  • 专用私有 VM提供的最佳性能和强大安全性

节省时间简化生活:只需 5 分钟即可试用 Stackhero 的 Redis 云托管 解决方案!

我们的一个客户提出了一个有趣的挑战。他运营一个体育网站,用户在赢得赌注或完成各种操作时可以获得积分。

他的目标是显示每个用户的排名,展示他们上方和下方的用户,并生成一个前100名的排行榜。拥有200万用户的社区,数据需要实时处理!

在Stackhero,我们喜欢解决这样的挑战。在本文中,我们将逐步引导您了解我们的解决方案。

传统数据库如MySQLPostgreSQLElasticsearch并非为低延迟排名任务设计。这促使我们探索其他选项。

我们选择了Redis,这是一种极快且可靠的内存数据库。根据DB-Engines,它是全球第七大使用最广泛的数据库,并且在“键值存储”类别中处于领先地位。

Redis提供多种数据模型。在这种情况下,“有序集合”尤为突出。

有序集合结合了键和分数。在我们的案例中,键是用户ID,分数代表用户的积分。

我们首先启动了一个Stackhero上的Redis服务。该服务在2分钟内即可使用最新稳定版本上线,提供按小时计费,并配有Redis Commander,一个方便的Web GUI。我们使用此界面验证了概念。

我们添加了三个具有示例ID和分数的用户,如下所示:

| 用户名 | 分数 | | - | - | | userId1 | 11 | | userId2 | 54 | | userId3 | 24 |

这些用户通过以下Redis命令被添加到一个名为usersScores的有序集合中:

ZADD usersScores 11 "userId1"
ZADD usersScores 54 "userId2"
ZADD usersScores 24 "userId3"

Redis Commander,Stackhero上提供的Redis实例的Web GUIRedis Commander,Stackhero上提供的Redis实例的Web GUI

接下来,我们检索了userId1的分数:

ZSCORE usersScores "userId1"
> 11

这确认了userId1的分数确实是11。之后,我们检查了userId1的排名:

ZREVRANK usersScores "userId1"
> 2

请记住,排名从0开始。这意味着排名如下:

| 用户名 | 分数 | 排名 | | - | - | - | | userId1 | 11 | 2 | | userId2 | 54 | 0 | | userId3 | 24 | 1 |

命令ZREVRANK返回了2,这正是我们对userId1的预期。

您还可以获取前几名。例如,要检索前2名用户(从排名0到排名1),请运行:

ZREVRANGE usersScores 0 1 WITHSCORES
> 1) userId2
> 2) 54
> 3) userId3
> 4) 24

要获取前100名用户,只需运行:

ZREVRANGE usersScores 0 99 WITHSCORES

这种方法高效且非常适合高性能实时排名。

以下是我们客户网站上使用的一些其他Redis命令:

  • 获取排名在50到100之间的用户:ZREVRANGE usersScores 50 100 WITHSCORES
  • 添加用户:ZADD usersScores 40 "userId4"
  • 更新用户的分数(这将替换现有的userId4条目):ZADD usersScores 42 "userId4"
  • 删除用户:ZREM usersScores "userId4"

在Redis Commander中验证概念后,是时候将Redis集成到实际代码中了。我们的客户使用Node.js,以下是使用ioredis作为客户端的示例:

const Redis = require('ioredis');

(async () => {
  // 设置Redis凭证
  // 如果您使用Stackhero,您将在Stackhero仪表板上找到这些
  const redis = new Redis({
    host: '<redisServerHost>',
    password: '<redisServerPassword>',
    port: <PORT_TLS>, // <PORT_CLEAR>用于非加密连接,<PORT_TLS>用于TLS。应使用TLS。
    tls: {}, // 提供一个空对象以激活TLS
    lazyConnect: true
  });

  // 连接到Redis
  await redis.connect();

  // 添加用户
  await redis.zadd('usersScores', 11, 'userId1');
  await redis.zadd('usersScores', 54, 'userId2');
  await redis.zadd('usersScores', 24, 'userId3');

  // 检索userId1的分数
  const score = await redis.zscore('usersScores', 'userId1');
  console.log('userId1有 ' + score + ' 分');

  // 检索userId1的排名位置
  const rankPosition = await redis.zrevrank('usersScores', 'userId1');
  console.log('userId1排名在位置 ' + rankPosition);

  // 从Redis断开连接
  await redis.disconnect();
})();

这个简单而强大的代码片段非常适合管理实时排名数据。

解决这个问题既有趣又具有挑战性。在我们的案例中,Redis被证明是一个理想的解决方案,因为它易于使用、功能强大且速度极快。

如果您想尝试Redis,您可以在Stackhero上启动一个实例,只需2分钟。享受最新的稳定版本、Web GUI、备份和令人印象深刻的性能,触手可及。

Stackhero仪表板显示正在运行的Node.js和Redis服务Stackhero仪表板显示正在运行的Node.js和Redis服务