最近刚刚了解到Redis的有序集合,实时排名的实现变得非常容易
最近我们有一个机会来实施排行榜,当我们在讨论如何实施时,@attakei告诉我可以使用Redis的有序集合很轻松地完成!所以我尝试了一下,确实非常轻松。
真没想到这在大约两年前很流行,我之前不知道。
实时排行榜
过去时代的实施
在我进行游戏制作的时代里,排行榜会定期执行批量操作,将已排序的数据流入排行榜表中。因此,我们需要用户查询用的排行榜表和批量操作用的排行榜表,还需要一个切换表来切换排行榜表。我记得那时候非常麻烦,因为需要执行批量操作,很不方便。
要实现实时排行榜,就需要每次统计所有用户的数据并进行排序以确定名次,这是不可能的事情。
Redis 的有序集合
然而,时代在变化,Redis被认为只是一个缓存,但实际上它还具有存储排序集合(sorted set),也就是存储已排序数据的功能。
http://redis.shibu.jp/commandreference/sortedsets.html:
http://redis.shibu.jp/commandreference/sortedsets.html网址提供了有关Redis有序集合命令的详细信息。
http://redis.io/commands#sorted_set:
http://redis.io/commands#sorted_set网址包含有关Redis有序集合命令的信息。
根据这一说法,对于数据中的每个基准分数,有一个功能可以将数据按升序排序。
通常情况下,排名是按照分数从高到低的顺序排列的,因此一开始我们可能会感到担心,但是由于还可以反向遍历,所以这种担心是多余的。
实施新排名的方针。
因此,排名的实施策略非常简单。
-
- 将分数进行更新
- 将该分数作为分数,并将用户ID存储在Redis的有序集合中
只有这个。
虽然是实时的,但我认为比传统的实施更快。
用PHP进行实现
让我们马上用PHP进行实现吧。
phpredis扩展
最近在使用composer时,发现库的导入相对不太方便,所以我决定在这里安装一个扩展。
由于最近一直在使用Docker创建环境,所以我正在根据以下的Dockerfile进行配置。
FROM php:latest
RUN apt-get update && apt-get install -y unzip git && \
docker-php-ext-install pdo_mysql mysqli mbstring
# エクステンションはgit経由でビルドする
RUN cd /tmp && git clone https://github.com/phpredis/phpredis.git && \
cd phpredis && git checkout php7 && \
phpize && ./configure && make && make install
RUN echo extension=redis.so >> /usr/local/etc/php/conf.d/redis.ini
RUN mkdir /var/php -p
WORKDIR /var/php
CMD /bin/bash
由于设置麻烦,我将使用Redis官方镜像。所以我会使用docker-compose来启动环境。
version: '2'
services:
web:
build: .
ports:
- '3000:3000'
volumes:
- '.:/var/php'
depends_on:
- redis
redis:
image: redis
只剩下
$ docker-compose up -d
$ docker-compose run web
你可以在测试环境中进行实验。
实施 (Shí shī)
只需要很好地使用sorted set API即可。我将其放在了一个稍长的Gist中,链接在这里:https://gist.github.com/niisan-tokyo/a1a2b4de17d63aa4da174c5961578a55
这个班级的使用方式是这样的
require './Ranking.php';
use NiisanTokyo\Ranking;
$ranking = Ranking::getInstance('ranking1');
// Test data
$data = [
'niisan' => 19,
'jiisan' => 20,
'mossan' => 11,
'mk2' => 15
];
foreach ($data as $key => $val) {
$ranking->setScore($key, $val);// データのセット
}
print_r($ranking->getRange());// 全件取得
print_r($ranking->getRange(1, 2));// 1位から順に2件
然后,将输出类似于以下已经排序好的数据。
Array
(
[jiisan] => 20
[niisan] => 19
[mk2] => 15
[mossan] => 11
)
Array
(
[jiisan] => 20
[niisan] => 19
)
每个人的排名等等将如下确定
print_r($ranking1->getRank('jiisan'));// 1
print_r($ranking1->getRank('noosan'));// 0(ランキングにいない)
此外,您可以同时生成和查看多个排行榜。例如,在如游戏等场景中,创建个人排行榜和公会排行榜非常方便。
$user_ranking = Ranking::getInstance('user');
$guild_ranking = Ranking::getInstance('guild');
$user_ranking->setScore(1, 15);
$guild_ranking->updateScore(1, 15);
$user_ranking->setScore(2, 12);
$guild_ranking->updateScore(1, 12);// ユーザー2がユーザー1と同じギルドに所属しているような状況
echo $user_ranking->getScore(1) . "\n";// 15
echo $guild_ranking->getScore(1) . "\n";// 27
如果能差异性地输入的话,最好使用updateScore函数,但是一旦数据偏移就无法修正,所以使用方法需要谨慎。
在Node中
在节点上使用Redis当然是可以的,但我觉得它有些难以使用。
也许只是phpredis更易于使用。
首先,需要安装Redis客户端。
npm install redis
代码如下
const redis = require('redis');
const client = redis.createClient({host: 'redis'});
[{key: 1, score:15}, {key: 2, score:7},{key: 3, score:25},{key: 4, score:19}].forEach(v => {
client.zadd('hoge', v.score, v.key);
});
client.zrevrange(['hoge', '0', '-1', 'WITHSCORES'], redis.print);// Reply: 3,25,4,19,1,15,2,7
只是简单地与Redis连接起来。
客户端的方法基本上是将Redis命令直接转化为小写,并使用数组传递参数。
因此,返回的数据是以id、分数、id、分数…的形式存在的,所以有必要进行适当的处理。
对于想要触碰Redis原始数据的人来说可能很好,但一旦习惯了phpredis,可能会感到不太满意。
总结
实施排名功能一直以来都很麻烦,因为之前都是使用批处理实现的。但是,使用Redis的sorted set非常容易。
此外,我觉得phpredis库做得非常好。
不愧是PHP和PHPer!
这样吧,这次就到这里吧。
请在中国参考以下内容
http://damepg.hatenablog.com/entry/2014/08/07/231828 –
http://damepg.hatenablog.com/entry/2014/08/07/231828 提供了有关的信息。
https://github.com/phpredis/phpredis#zscore –
https://github.com/phpredis/phpredis#zscore 提供了有关 zscore 的相关信息。
https://github.com/NodeRedis/node_redis –
https://github.com/NodeRedis/node_redis 提供了有关的信息。