Tutorials  /  Databases

Real-Time Water Billing with PHP, Redis Pub/Sub & MySQL

Ccentron Redaktion · April 2025 ·7 min read ·Databases, Tutorial

Redis® Pub/Sub is an architectural model that enables publishers to transmit data to subscribers through designated channels. This technology is especially useful for constructing real-time apps that depend on Inter-Process Communications (IPCs), such as financial platforms, live chat systems, and more. In such scenarios, low latency is essential—users anticipate near-instantaneous delivery of messages when connected to a channel. When building applications with traditional SQL databases, performance bottlenecks can arise due to scalability limitations. In contrast, Redis® operates using your server's RAM and is capable of performing numerous read/write operations per second.

Another major benefit of utilizing Redis® for the Pub/Sub model is the decoupling of publishers and subscribers. This setup allows data to be sent to a channel without explicitly specifying the recipients. Similarly, subscribers can listen to channels and await messages, independent of any knowledge about the sources of those messages.

In such loosely integrated systems, frontend and backend developers can independently build and test their components. This approach shortens development time since different teams work in parallel, increases developer autonomy, and simplifies hiring due to the reduced complexity of the system architecture.

This tutorial walks you through creating a modular water billing system using PHP and Redis®. In this setup, a frontend component (acting as the publisher) collects water usage data from households and transmits it via a billing channel. On the backend, a subscriber script processes this information and stores it permanently in a MySQL database.

Prerequisites

Ensure the following prerequisites are met to follow along with this Redis® Pub/Sub guide:

  • Ubuntu 20.04 server
  • A user with sudo privileges
  • An installed LAMP stack
  • Redis® Server
DB

Matching infrastructure at centron

Databases need care in production: centron can take over updates, backups and monitoring – from a single instance to a full cluster. Explore managed clusters →

1. Install the PHP Redis® Extension

To allow your PHP scripts to communicate with Redis®, install the php-redis extension. Begin by accessing your server via SSH and updating the package index:

Console
$ sudo apt update

Then install the extension:

Console
$ sudo apt install -y php-redis

Restart Apache to apply the changes:

Console
$ sudo systemctl restart apache2

With the environment ready for PHP-Redis® communication, proceed to database creation.

2. Set Up a Sample Database and User

Redis® stores information in memory and while it can save data to disk, its core purpose is high-speed, transient data handling. In this billing application, Redis® will quickly collect consumption data from users which you'll later store permanently in MySQL.

Log into MySQL as root:

Console
$ sudo mysql -u root -p

Enter your MySQL root password. Run the following to create the billing_db database and a user. Replace EXAMPLE_PASSWORD with a secure password:

Code
mysql> CREATE DATABASE billing_db;
       CREATE USER 'billing_db_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
       GRANT ALL PRIVILEGES ON billing_db.* TO 'billing_db_user'@'localhost';           
       FLUSH PRIVILEGES;

You should see output like:

Query OK, 0 rows affected (0.00 sec)

Switch to the new database:

Code
mysql> USE billing_db;

The response should confirm:

Database changed

Create the customers table to store user information:

Code
mysql> CREATE TABLE customers (
           customer_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
           first_name VARCHAR(50),
           last_name VARCHAR(50)           
       ) ENGINE = InnoDB;

Expected result:

Query OK, 0 rows affected (0.01 sec)

Add sample records:

SQL
mysql> INSERT INTO customers (first_name, last_name) values ('JOHN', 'DOE');
       INSERT INTO customers (first_name, last_name) values ('MARY', 'SMITH');
       INSERT INTO customers (first_name, last_name) values ('STEVE', 'JONES');

Each successful insert should show:

Query OK, 1 row affected (0.01 sec)

Now define the billings table to store monthly usage. The ref_id uniquely identifies each bill. billing_period denotes the billing month’s end. units_consumed tracks water usage in cubic meters. cost_per_unit holds the billing rate in USD.

Code
mysql> CREATE TABLE billings (
           ref_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
           customer_id BIGINT,
           billing_period VARCHAR(50),
           units_consumed DOUBLE,
           cost_per_unit  DOUBLE          
       ) ENGINE = InnoDB;

You should see:

Query OK, 0 rows affected (0.02 sec)

Exit MySQL CLI:

Code
mysql> QUIT;

Output:

Bye

You’ve now initialized the database, structured essential tables, and populated some demo entries. The next stage involves coding the frontend component that captures water consumption data from clerks to initiate the billing cycle.

3. Develop a PHP-Based Frontend Script

In this billing platform, household water consumption is tracked in cubic meters. Unless smart metering solutions are in place, data clerks must manually record readings and forward them to the company’s backend systems.

Today, such data can be transmitted through mobile apps communicating with APIs hosted on cloud servers. In this section, you will craft a frontend script using PHP to handle this data. Start by creating a new file called frontend.php in the web server’s root directory using the following:

Console
$ sudo nano /var/www/html/frontend.php

Paste the following script into that file:

PHP
<?php 

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

        $channel      = 'billings';
        $billing_data = file_get_contents('php://input');          

        $redis->publish($channel, $billing_data); 

        echo "Data successfully sent to the billings channel.\n";         

        } catch (Exception $e) {
            echo $e->getMessage();
        }

Save and exit the editor. Here's how the code functions:

  • It connects to Redis at 127.0.0.1:6379 using:

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

  • It captures incoming JSON data with:

    $billing_data = file_get_contents('php://input');

  • The data is sent to the billings channel using:

    $redis->publish($channel, $billing_data);

  • If any issues arise, the catch block handles them gracefully.

You’ve now set up a script that collects billing data and pushes it to a Redis® channel. The next task is building the backend script.

4. Create the PHP Backend Script

To process incoming billing records and persist them to MySQL, you will subscribe to the billings channel using a script called backend.php. Start by creating the file:

Console
$ sudo nano /var/www/html/backend.php

Insert the script below:

PHP
<?php 

    try {   
             
        $redis = new Redis(); 
        $redis->connect('127.0.0.1', 6379);
        $redis->setOption(Redis:: OPT_READ_TIMEOUT, -1);       

        $redis->subscribe(['billings'], function($instance, $channelName, $message) { 
         
            $billing_data = json_decode($message, true); 

            $db_name     = 'billing_db';
            $db_user     = 'billing_db_user';
            $db_password = 'EXAMPLE_PASSWORD';
            $db_host     = 'localhost';

            $pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);   

            $sql = 'insert into billings(
                        customer_id, 
                        billing_period, 
                        units_consumed,
                        cost_per_unit
                    ) 
                    values(
                        :customer_id, 
                        :billing_period, 
                        :units_consumed,
                        :cost_per_unit
                    )'; 

            $data = [];

            $data = [
                    'customer_id'    => $billing_data['customer_id'],
                    'billing_period' => $billing_data['billing_period'],
                    'units_consumed' => $billing_data['units_consumed'],
                    'cost_per_unit'  => 2.5                                 
                    ];

            $stmt = $pdo->prepare($sql);
            $stmt->execute($data); 

            echo "The Redis® data was sent to the MySQL database successfully.\n" ;               
                    
        });         

        } catch (Exception $e) {
            echo $e->getMessage();
        }

This backend script:

  • Establishes a connection to Redis®.
  • Subscribes to the billings channel.
  • Disables timeout to keep the listener alive indefinitely.
  • Parses incoming JSON messages.
  • Prepares and executes a SQL INSERT to store the message data into MySQL.

5. Run a Test for Redis® Pub/Sub

Open two terminal windows. In the first, launch the backend listener:

Console
$ php /var/www/html/backend.php

Note: this script will remain running as it continuously waits for incoming messages.

In the second terminal, use the following curl commands to simulate data submission:

Console
$ curl -X POST http://localhost/frontend.php -H 'Content-Type: application/json' -d '{"customer_id":1, "billing_period": "2021-08-31", "units_consumed":12.36}'
$ curl -X POST http://localhost/frontend.php -H 'Content-Type: application/json' -d '{"customer_id":2, "billing_period": "2021-08-31", "units_consumed":40.20}'
$ curl -X POST http://localhost/frontend.php -H 'Content-Type: application/json' -d '{"customer_id":3, "billing_period": "2021-08-31", "units_consumed":24.36}'

Each successful post should return:

Data successfully sent to the billings channel.

The backend terminal should simultaneously display:

The Redis® data was sent to the MySQL database successfully.

To verify, log into the MySQL server:

Console
$ mysql -u billing_db_user -p

Use your user password (e.g., EXAMPLE_PASSWORD). Then run:

Code
mysql> USE billing_db;

Expected output:

Database changed.

Then, retrieve billing records with this SQL JOIN:

Code
mysql> SELECT
           billings.ref_id as bill_reference_no,
           customers.customer_id,
           customers.first_name,
           customers.last_name,
           billings.billing_period,
           billings.units_consumed,
           billings.cost_per_unit,
           CONCAT('$', (billings.units_consumed * billings.cost_per_unit)) as bill_amount
       FROM billings
       LEFT JOIN customers
       ON billings.customer_id = customers.customer_id;

You should receive output similar to this:

+-------------------+-------------+------------+-----------+----------------+----------------+---------------+-------------+
| bill_reference_no | customer_id | first_name | last_name | billing_period | units_consumed | cost_per_unit | bill_amount |
+-------------------+-------------+------------+-----------+----------------+----------------+---------------+-------------+
|                 1 |           1 | JOHN       | DOE       | 2021-08-31     |          12.36 |           2.5 | $30.9       |
|                 2 |           2 | MARY       | SMITH     | 2021-08-31     |           40.2 |           2.5 | $100.5      |
|                 3 |           3 | STEVE      | JONES     | 2021-08-31     |          24.36 |           2.5 | $60.9       |
+-------------------+-------------+------------+-----------+----------------+----------------+---------------+-------------+
3 rows in set (0.00 sec)

Conclusion

Throughout this tutorial, you’ve utilized Redis® Pub/Sub to transmit and process water usage data on Ubuntu 20.04 using PHP. The data was successfully routed from Redis® to a MySQL database, showcasing Redis®’s powerful ability to decouple system components in real-time applications.

Jetzt 200 € Guthaben sichern

Testen Sie Ihr Setup auf ccloud³

Registrieren Sie sich in der ccloud³ und erhalten Sie 200 € Startguthaben für Ihr Projekt – z. B. für eine PostgreSQL-VM mit automatischen Backups.

centron Redaktion Technische Redaktion

Das Redaktionsteam von centron schreibt Anleitungen aus dem Betriebsalltag: getestet auf unserer eigenen Plattform, betrieben im Rechenzentrum in Hallstadt bei Bamberg.

Kategorie Databases
Teilen
Noch offene Fragen?

Our team will help you with your specific setup - in German or English, by people who run the platform themselves.

War dieses Tutorial hilfreich?

Your answer is stored anonymously and helps us improve our tutorials.

Kommentare

No comments yet - be the first to ask a question about this tutorial.

Sign in to comment

Comments are open to centron customers. Sign in to your account to ask a question about this tutorial.

Weiterlesen

Das könnte Sie auch interessieren

Jetzt kostenlos anfangen

Melden Sie sich an und erhalten Sie in den ersten 60 Tagen ein Guthaben von 200 € bei centron.

Dieses Werbeangebot gilt nur für neue Konten. Angebot ausschließlich für Gewerbetreibende.

Jetzt loslegen Sales kontaktieren