Skip to content

Os14you/Redis-cpp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Redis-cpp: A Redis Clone in C++

This project is a lightweight, high-performance, in-memory key-value store inspired by Redis, built from scratch in modern C++. It features a non-blocking, single-threaded server architecture and a custom hash table implementation with incremental rehashing to ensure low-latency operations.

✨ Features

  • Asynchronous Networking: A single-threaded, event-driven server built using the poll() system call for high-concurrency I/O without the overhead of threads.
  • Efficient Data Structures:
    • A custom-built Hash Table that uses incremental rehashing to avoid long pauses and latency spikes when the table needs to be resized.
    • A self-balancing AVL Tree to maintain sorted data and enable efficient rank-based queries.
  • Multiple Data Types: Support for common Redis data types:
    • Strings: Basic key-value pairs.
    • Sorted Sets (ZSETs): A collection of unique members, each associated with a score, ordered by that score. Implemented with a hash table and an AVL tree for optimal performance.
  • Client-Server Architecture: Includes both a server (redis-server) and a command-line client (redis-cli) for interaction.
  • Simple Binary Protocol: A custom, lightweight, length-prefixed binary protocol for efficient communication between the client and server.
  • Core Redis Commands: Implementation of several essential Redis commands (non-case sensitive), listed below.

⌨️ Supported Commands

General

  • KEYS: Returns all keys in the database.
  • DEL <key>: Deletes a key.
  • PING [message]: Checks server responsiveness.

String

  • SET <key> <value>: Sets the string value of a key.
  • GET <key>: Gets the value of a key.

Sorted Set (ZSET)

  • ZADD <key> <score> <member> [<score> <member> ...]: Adds one or more members to a sorted set, or updates its score if it already exists.
  • ZREM <key> <member> [<member> ...]: Removes one or more members from a sorted set.
  • ZRANGE <key> <start> <end>: Returns the specified range of members in the sorted set, ordered from low to high scores.
  • ZREVRANGE <key> <start> <end>: Returns the specified range of members, ordered from high to low scores.
  • ZSCORE <key> <member>: Returns the score of a member in a sorted set.

🏗️ Project Structure

The repository is organized into the following directories:

.
├── include/
│   ├── common/     # Shared utilities (Serialization, Deserialization)
│   ├── core/       # Core data structures (HashTable)
│   ├── net/        # Networking library (Server, Client, Network)
│   └── server/     # Redis application logic
├── src/
│   ├── common/
│   ├── core/
│   ├── net/
│   ├── server/
│   ├── redis-cli.cpp    # Client entry point
│   └── server-main.cpp  # Server entry point
├── bin/              # Compiled executables (created after build)
├── build/            # Object files (.o) (created after build)
└── Makefile          # Build script
  • include/: Contains all header files, defining the interfaces for the server, client, network layer, and data structures.
  • src/: Contains the C++ source code implementing the logic defined in the headers.
  • bin/ and build/: These directories are created by the Makefile to store the final executables and intermediate object files, respectively.

🚀 Getting Started

Follow these instructions to build and run the project on a Linux-based system.

Prerequisites

  • A C++17 compliant compiler (e.g., g++)
  • make

Build

To compile the server and client executables, simply run the make command from the root of the project directory:

make

This will compile all source files and place the redis-server and redis-cli executables in the bin/ directory.

Running the Server

Start the Redis server by running its executable. It will listen on the default Redis port 6379.

./bin/redis-server

You will see a confirmation message once the server is running:

Server listening on port 6379 ...

Using the Command-Line Client (CLI)

Open a new terminal and use the redis-cli to interact with the server. Here are some example commands:

String Commands

# SET: Store a key-value pair
$ ./bin/redis-cli SET myKey "Hello C++"
> SET myKey "Hello C++" 
(nil)

# GET: Retrieve the value for a key
$ ./bin/redis-cli GET myKey
> GET myKey 
"Hello C++"

Sorted Set Commands

# ZADD: Add members to a sorted set
$ ./bin/redis-cli ZADD leaderboard 100 alice 200 bob 150 charlie
> ZADD leaderboard 100 alice 200 bob 150 charlie
(integer) 3

# ZRANGE: Get members by rank (ordered by score)
$ ./bin/redis-cli ZRANGE leaderboard 0 -1
> ZRANGE leaderboard 0 -1
(arr) 3 elements:
 "alice"
 "charlie"
 "bob"

# ZSCORE: Get the score of a specific member
$ ./bin/redis-cli ZSCORE leaderboard charlie
> ZSCORE leaderboard charlie
"150.000000"

# ZREM: Remove a member
$ ./bin/redis-cli ZREM leaderboard alice
> ZREM leaderboard alice
(integer) 1

# Check the set again
$ ./bin/redis-cli ZRANGE leaderboard 0 -1
> ZRANGE leaderboard 0 -1
(arr) 2 elements:
 "charlie"
 "bob"

Cleaning Up

To remove all compiled files (from bin/ and build/ directories), run:

make clean

🏛️ Architecture

Event Loop and Networking

The server operates on a single thread, using an event loop powered by poll(). This allows it to manage multiple client connections concurrently without blocking. All I/O operations are non-blocking, ensuring that the server remains responsive even under load. The core logic is contained within the Server::run() method.

Data Storage

The in-memory data store is built on a primary HashTable that maps string keys to values. The values are stored in a std::variant, allowing each key to hold different data types, such as a simple string or a complex SortedSet.

  • Hash Table with Incremental Rehashing: The primary key-value store. To handle resizing without causing performance degradation, it implements incremental rehashing. When the table's load factor exceeds a threshold, entries are migrated gradually from the old table to a new, larger one with every subsequent operation. This amortizes the cost of resizing, leading to smoother performance.
  • Sorted Set Implementation: The SortedSet data type is implemented using a combination of two data structures for maximum efficiency:
    • A Hash Table maps members to their scores, providing $O(1)$ average time complexity for score lookups (ZSCORE).
    • A self-balancing AVL Tree stores members sorted by their scores, enabling efficient $O(log N)$ operations for adding, removing, and executing range queries (ZADD, ZREM, ZRANGE).

📄 License

This project is licensed under the GNU General Public License v3.0. A copy of the license is available in the LICENSE file in the repository.

About

A high-performance Redis clone in C++17, featuring a non-blocking server, custom hash table with incremental rehashing, and core Redis commands. Built from scratch for learning and performance.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Contributors