Create a Leaderboard with Redis and PHP on Ubuntu 20.04

Updated on September 9, 2021
Create a Leaderboard with Redis and PHP on Ubuntu 20.04 header image

Introduction

You can use a leaderboard to rank players in gamified systems. It's an essential part of building an application where several participants are competing for a specific goal. Leaderboards are widely used in computer games to show peers how they rank compared to other players.

Today, you can use leaderboards in a variety of applications to rank performances. For instance, you might use it to rank salespersons depending on the total sales they've made within a given period. Similarly, you can implement a leaderboard in a fitness tracker application, and anyone connected to your software can compete in fitness leagues and online challenges. In the financial industry, you can use leaderboards to monitor and detect suspicious transactions and mitigate fraud.

The best thing about leaderboards is creating a sense of motivation; they encourage participants to work hard and achieve their goals. In addition, in some sense, leaderboards build cohesion since participants are grouped with like-minded peers depending on the criteria of each competition.

While leaderboards are great, consuming and processing data from thousands or even millions of users connected to your application to provide real-time analytics may not work well with traditional SQL databases since they save data to disk. For this task, you require an in-memory database that can read, write, slice, and sort data at a massive scale without any disk IO overhead. This is where the Redis Server comes in.

Redis is highly suitable for fast data ingest and can handle complex mathematical computations (for instance, sorted sets and attributes that you require to analyze and rank data to create a leaderboard. It can also handle simultaneous clients performing thousands of requests per second. To put this in a better perspective, Redis can effectively process step counts data from thousands of fitness devices at a lightning speed.

In this guide, you'll create a PHP logic that uses the php-redis library to accept data and create a leaderboard on an Ubuntu 20.04 server.

Prerequisites

Before you proceed, make sure you have the following:

  • An Ubuntu 20.04 server. Since Redis uses your computer memory for storage, spin up a server with several vCPUs and a fair amount of RAM.
  • A sudo user.
  • A Lamp Stack. For this guide, you might skip Step 2 (Install a Database Server) since you don't need a MySQL/MariaDB server.
  • A Redis Server

1. Install the Redis Extension For PHP

In this step, you'll install a PHP extension that allows PHP scripts to talk to the Redis Server via an Application Programming Interface (API). This is a fast, flexible, fully functional, and user-friendly Redis library.

To install the library, SSH to your server and update the package information index.

$ sudo apt update

Next, run the following command to install the php-redis extension.

$ sudo apt install -y php-redis

Restart the Apache webserver to load the new extension by executing the following command.

$ sudo systemctl restart apache2

You now have a working PHP extension for interacting with Redis. Next, you'll create a PHP script that accepts data for ranking purposes.

2. Create a PHP Script To Accept User Scores

Before you start ranking your data in either ascending or descending order, you should use the Redis ZADD command to add members with their respective scores to a sorted set ZSET. If you have already included a member in a set, you can update their respective score at any time by calling the ZADD command against the existing member's data. Behind the scenes, the Redis server will update the score and position of the member. The ZADD command is suitable in situations where you have tons of updates since it is extremely fast.

To add a member's score via the redis-server command-line interface, you should use the syntax below.

$ redis-cli

127.0.0.1:6379> ZADD NAME_OF_THE_SORTED_SET SCORE_VALUE MEMBER_NAME
127.0.0.1:6379> quit

For example, to add a score of 1768 to a member named Fred, use the syntax below.

$ redis-cli

127.0.0.1:6379> ZADD players 1768 Fred
127.0.0.1:6379> quit

In this guide, you'll create a PHP script that automates the whole process using the Redis zadd function. Use the nano text editor to open a new zadd.php file in the root directory of your web server by executing the following command.

$ sudo nano /var/www/html/zadd.php

Then, enter the information below into the file.

<?php 

$redis = new Redis(); 
$redis->connect('127.0.0.1', 6379); 

$seller      = $_POST['seller'];
$total_sales = $_POST['total_sales'];

$redis->zadd('sales_persons', $total_sales, $seller);

echo "You've successfully added the seller's sales into the Redis server.\n";

Save and close the file when you're through with editing. In the above file, you're connecting to the Redis server. Then you're listening for two HTTP POST variables from the client's applications. The variable $_POST['seller'] holds the member's name that you wish to add into the sorted set. Then, you're retrieving the current sales amount of a salesperson (score) from the $_POST['total_sales'] variable.

In this script, you're simply accepting salesperson data depending on the sales an employee has made. Then, you're adding the data of each participating employee into the sorted set named sales_persons.

Next, you'll simulate some sales records using the Linux curl command. You might find it easier to feed sales data from your invoicing software through an API in a production environment instead of adding it manually, especially if you have many salespeople, such as a web hosting referral system.

For now, execute the following 7 commands against the zadd.php script to add the salespersons' data to the Redis server.

$ curl --data "seller=JOHN DOE&total_sales=9749" http://localhost/zadd.php
$ curl --data "seller=MARTHA SMITH&total_sales=1822" http://localhost/zadd.php
$ curl --data "seller=PETER JACOB&total_sales=4437" http://localhost/zadd.php
$ curl --data "seller=ESTHER ERIC&total_sales=6849" http://localhost/zadd.php
$ curl --data "seller=JANE ERIC&total_sales=2256" http://localhost/zadd.php
$ curl --data "seller=MARY PATRICK&total_sales=7894" http://localhost/zadd.php
$ curl --data "seller=PETER MARTIN&total_sales=3526" http://localhost/zadd.php

Ensure you receive the following output after running each command to make sure the data is in place.

...
You've successfully added the seller's sales into the Redis server.

You now have a working Redis sorted set, which you've populated with some records. Next, you'll use a Redis ZREVRANGE function to rank the data.

3. Create and Test a PHP Leaderboard Script

You should order the sales amount from the highest to the lowest value to rank your sales data. That is, in descending order. You can accomplish this task in a command-line interface by executing a ZREVRANGE against your sorted set using the following syntax.

$ redis-cli

127.0.0.1:6379> ZREVRANGE NAME_OF_THE_SORTED_SET START END WITHSCORES

For instance, to get a list of all salespersons arranged in ascending order together with their associated sales amount between $0 and $200000, run the following commands.

$ redis-cli

127.0.0.1:6379> ZREVRANGE sales_persons 0 200000 WITHSCORES

You should get the following output.

 1) "JOHN DOE"
 2) "9749"
 3) "MARY PATRICK"
 4) "7894"
 5) "ESTHER ERIC"
 6) "6849"
 7) "PETER JACOB"
 8) "4437"
 9) "PETER MARTIN"
 10) "3526"
 11) "JANE ERIC"
 12) "2256"
 13) "MARTHA SMITH"
 14) "1822"

Exit from the Redis server command-line interface.

127.0.0.1:6379> quit

You'll accomplish the same result above by using the zRevRange function inside a PHP script. This time around, you will structure the leaderboard in a more human-readable format using some PHP functions.

To do this, use nano to open a blank /var/www/html/leaderboard.php file.

$ sudo nano /var/www/html/leaderboard.php

Next, enter the information below into the /var/www/html/leaderboard.php file.

<?php 

$redis = new Redis(); 
$redis->connect('127.0.0.1', 6379);     

$result = $redis->zRevRange('sales_persons', 0, 200000, true);      

echo  "\n" . str_repeat("-", 40);  
echo "\nSALES RANK BY HIGHEST SELLER\n";
echo  str_repeat("-", 40) . "\n";
echo "\nRANK  NAME                         SCORE \n";
echo  str_repeat("=", 40) . "\n";

$i = 1;

foreach($result as $key => $value) {
    echo str_pad($i, 6, " ") . str_pad($key, 17, " ") . str_pad($value, 17, " ", STR_PAD_LEFT) . "\n";
    $i++;
}

Save and close the file. In the above file, you're initializing a new Redis instance. Then, you're invoking the $redis->zRevRange function to retrieve the sales data made by sellers between $0 and $200000 from the sales_persons sorted set using the statement $result = $redis->zRevRange('sales_persons', 0, 200000, true);.

The statement $redis->zRevRange returns the sales data as an array, and you're using the PHP foreach(...) statement to echo out the name of the salespersons and their associated sales amount. Then, you're using the PHP str_repeat and str_pad functions to format the array into a human-readable report.

To test the leaderboard.php script, execute the leaderboard.php script using the Linux curl command.

$ curl http://localhost/leaderboard.php

You should now get the following output. As you can see, JOHN DOE leads in sales followed by MARY PATRICK.

----------------------------------------
SALES RANK BY HIGHEST SELLER
----------------------------------------

RANK  NAME                         SCORE
========================================
1     JOHN DOE                      9749
2     MARY PATRICK                  7894
3     ESTHER ERIC                   6849
4     PETER JACOB                   4437
5     PETER MARTIN                  3526
6     JANE ERIC                     2256
7     MARTHA SMITH                  1822

To test whether the leaderboard is able to handle real-time data as it changes, execute the following curl commands to add 2 more sales records.

$ curl --data "seller=MARTHA KATE&total_sales=3940" http://localhost/zadd.php
$ curl --data "seller=ZABRON JAMES&total_sales=12400" http://localhost/zadd.php

Output.

...
You've successfully added the seller's sales into the Redis server.

Execute the leaderboard.php script again to check whether the Redis server has updated the rankings.

$ curl http://localhost/leaderboard.php

As you can see from the output below JOHN DOE has been overtaken by ZABRON JAMES.

----------------------------------------
SALES RANK BY HIGHEST SELLER
----------------------------------------

RANK  NAME                         SCORE
========================================
1     ZABRON JAMES                 12400
2     JOHN DOE                      9749
3     MARY PATRICK                  7894
4     ESTHER ERIC                   6849
5     PETER JACOB                   4437
6     MARTHA KATE                   3940
7     PETER MARTIN                  3526
8     JANE ERIC                     2256
9     MARTHA SMITH                  1822

The above output confirms that indeed, the Redis server is well optimized and suitable to handle leaderboards. In a production environment, you should poll the leaderboard.php script and display the result in a client application after every few seconds to get updated ranks for the salespersons. This would encourage healthy competition as the employees struggle to appear on top of the list.

In this guide, you're using a Redis leaderboard to check and probably reward hardworking salespersons. This logic is very useful in other different scenarios. For instance, if you have a subscription service where you're accepting credit cards, you can use a leaderboard to check the highest-ranking orders originating from a single customer to detect fraud. Similarly, in a multi-author blog, you can use a scoreboard to watch the highest performing bloggers in real-time based on the unique page views or hits originating from their blog posts.

Conclusion

In this guide, you've built a real-time leaderboard script with PHP on a Redis server running on Ubuntu 20.04. Since relational database management systems like MySQL perform sub-optimally for leaderboards, you should use the Redis server since it's optimized to handle thousands of writes per second. Redis in-memory processing was designed with speed in mind and is the most suitable platform for handling thousands of simultaneous users with a near-instant speed.