C++项目推荐-真正可以媲美redis的kv存储项目-包括性能如何逐步优化

概述

本教程将手把手带你从零实现一个高性能的Mini-Redis,涵盖RESP协议解析、事件驱动网络编程、数据结构实现、持久化和主从复制等核心技术。

这个kv存储项目是可以真正写入简历的C++项目,不同于网上其它的kv存储项目,加了持久化后,QPS测试性能只能到一千多,而这个项目可以达到55000+,性能可以媲美redis

视频讲解及源码领取:****************************-**********

项目目标

  • 协议兼容: 支持标准RESP协议,兼容redis-cli工具
  • 高性能: 单机QPS达5万+,AOF开启后仍保持高性能
  • 完整功能: 数据结构、持久化、过期、主从复制
  • 教学导向: 代码清晰,文档详细,适合学习

技术栈

  • 语言: C++17
  • 网络: epoll事件驱动
  • 协议: RESP(Redis Serialization Protocol)
  • 数据结构: unordered_map + 跳表
  • 持久化: AOF + RDB
  • 构建: CMake

第一章:项目架构设计

1.1 整体架构

1.2 请求处理流程

一个完整的请求经历以下阶段:

  1. 网络接收: epoll监听客户端连接和数据
  2. 协议解析: 解析RESP格式的命令
  3. 命令执行: 在KV存储中执行操作
  4. 持久化: AOF记录命令,RDB定期快照
  5. 主从复制: 同步命令到从节点
  6. 响应返回: 将结果序列化为RESP格式返回

1.3 模块划分

第二章:环境准备与项目搭建

2.1 环境要求

# Ubuntu/Debian
sudo apt install build-essential cmake pkg-config
​
# CentOS/RHEL
sudo yum install gcc-c++ cmake make
​
# 检查版本
g++ --version  # 需要支持C++17
cmake --version  # 建议3.15+

2.2 项目结构创建

mkdir mini-redis && cd mini-redis
mkdir -p include/mini_redis src docs tools build

项目目录结构:

mini-redis/
├── CMakeLists.txt          # CMake构建文件
├── include/mini_redis/     # 头文件
│   ├── config.hpp         # 配置结构体
│   ├── resp.hpp           # RESP协议
│   ├── kv.hpp             # KV存储
│   ├── aof.hpp            # AOF持久化
│   ├── rdb.hpp            # RDB持久化  
│   └── replica_client.hpp # 主从复制
├── src/                   # 源文件
│   ├── main.cpp          # 程序入口
│   ├── server.cpp        # 服务器主逻辑
│   ├── resp.cpp          # RESP实现
│   ├── kv.cpp            # KV存储实现
│   ├── aof.cpp           # AOF实现
│   ├── rdb.cpp           # RDB实现
│   ├── replica_client.cpp # 复制实现
│   └── config_loader.cpp # 配置加载
├── docs/                 # 文档
├── tools/                # 工具脚本
└── build/               # 构建目录

2.3 CMake配置文件

创建 CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(mini_redis)
​
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
​
# 编译选项
set(CMAKE_CXX_FLAGS "-Wall -Wextra -g")
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native")
​
# 头文件路径
include_directories(include)
​
# 源文件
set(SOURCES
    src/main.cpp
    src/server.cpp
    src/resp.cpp
    src/kv.cpp
    src/aof.cpp
    src/rdb.cpp
    src/replica_client.cpp
    src/config_loader.cpp
)
​
# 可执行文件
add_executable(mini_redis ${SOURCES})
​
# 链接库
find_package(Threads REQUIRED)
target_link_libraries(mini_redis PRIVATE Threads::Threads)

2.4 编译和使用

2.4.1 构建项目

cd mini-redis
cmake -S . -B build
cmake --build build -j

2.4.2 单机启动模式

Mini-Redis 支持三种持久化配置模式:

1. 无持久化模式 (none.conf)

适用于:测试、缓存场景,不需要数据持久化

# 启动服务器
./build/mini_redis --config build/none.conf
​
# 服务器将在端口 6388 启动,无 AOF 和 RDB

2. 每秒同步模式 (everysec.conf)

适用于:生产环境,平衡性能和数据安全

# 启动服务器
./build/mini_redis --config build/everysec.conf
​
# 服务器将在端口 6388 启动,AOF 每秒同步一次

3. 立即同步模式 (always.conf)

适用于:对数据一致性要求极高的场景

# 启动服务器  
./build/mini_redis --config build/always.conf
​
# 服务器将在端口 6388 启动,每个写操作立即同步到磁盘

配置文件详情

none.conf

port=6388
aof.enabled=false
rdb.enabled=false

everysec.conf

port=6388
aof.enabled=true
aof.mode=everysec
aof.dir=./data
aof.filename=appendonly.aof
rdb.enabled=false
aof.batch_bytes=262144
aof.batch_wait_us=2000
aof.prealloc_bytes=67108864
aof.sync_interval_ms=250

always.conf

port=6388
aof.enabled=true
aof.mode=always
aof.dir=./data
aof.filename=appendonly.aof
rdb.enabled=false

2.4.3 主从复制模式

主节点启动

创建主节点配置文件 master.conf

port=6379
bind_address=0.0.0.0
​
# AOF 持久化
aof.enabled=true
aof.mode=everysec
aof.dir=./data-master
aof.filename=appendonly.aof
​
# RDB 快照
rdb.enabled=true
rdb.dir=./data-master
rdb.filename=dump.rdb

启动主节点:

./build/mini_redis --config master.conf

从节点启动

创建从节点配置文件 replica.conf

port=6380
bind_address=0.0.0.0
​
# RDB 用于接收主节点快照
rdb.enabled=true
rdb.dir=./data-replica
rdb.filename=dump.rdb
​
# 从节点一般不开启 AOF
aof.enabled=false
​
# 复制配置
replica.enabled=true
replica.master_host=127.0.0.1
replica.master_port=6379

启动从节点:

./build/mini_redis --config replica.conf

2.4.4 使用 redis-cli 进行测试

连接测试

# 连接到单机模式
redis-cli -p 6388
​
# 连接到主节点
redis-cli -p 6379
​
# 连接到从节点
redis-cli -p 6380

基本命令测试

连接和状态

# 测试连接
redis-cli -p 6388 PING
​
# 获取服务器信息
redis-cli -p 6388 INFO
​
# 回显测试
redis-cli -p 6388 ECHO "Hello Mini-Redis"

String 操作

# 设置键值
redis-cli -p 6388 SET mykey "Hello World"
​
# 获取值
redis-cli -p 6388 GET mykey
​
# 删除键
redis-cli -p 6388 DEL mykey
​
# 设置过期时间(秒)
redis-cli -p 6388 SET tempkey "temporary" 
redis-cli -p 6388 EXPIRE tempkey 60
​
# 查看剩余过期时间
redis-cli -p 6388 TTL tempkey
​
# 检查键是否存在
redis-cli -p 6388 EXISTS mykey

Hash 操作

# 设置 Hash 字段
redis-cli -p 6388 HSET user:1 name "Alice"
redis-cli -p 6388 HSET user:1 age "25"
redis-cli -p 6388 HSET user:1 city "Beijing"
​
# 获取 Hash 字段
redis-cli -p 6388 HGET user:1 name
​
# 获取所有字段和值
redis-cli -p 6388 HGETALL user:1
​
# 检查字段是否存在
redis-cli -p 6388 HEXISTS user:1 email
​
# 删除字段
redis-cli -p 6388 HDEL user:1 age
​
# 获取字段数量
redis-cli -p 6388 HLEN user:1

ZSet (有序集合) 操作

# 添加成员和分数
redis-cli -p 6388 ZADD leaderboard 100 "player1"
redis-cli -p 6388 ZADD leaderboard 85 "player2" 
redis-cli -p 6388 ZADD leaderboard 92 "player3"
​
# 按分数范围查询(默认升序)
redis-cli -p 6388 ZRANGE leaderboard 0 -1
​
# 按分数范围查询并显示分数
redis-cli -p 6388 ZRANGE leaderboard 0 -1 WITHSCORES
​
# 获取成员分数
redis-cli -p 6388 ZSCORE leaderboard "player2"
​
# 删除成员
redis-cli -p 6388 ZREM leaderboard "player2"

其他操作

# 列出所有键
redis-cli -p 6388 KEYS "*"
​
# 清空所有数据
redis-cli -p 6388 FLUSHALL
​
# 触发 RDB 快照保存
redis-cli -p 6388 BGSAVE

主从复制测试

1. 在主节点写入数据

# 连接主节点并写入
redis-cli -p 6379 SET repl:test "master-data"
redis-cli -p 6379 HSET repl:hash field1 "value1"
redis-cli -p 6379 ZADD repl:zset 90 "item1"

2. 在从节点读取数据

# 连接从节点并读取
redis-cli -p 6380 GET repl:test
redis-cli -p 6380 HGETALL repl:hash  
redis-cli -p 6380 ZRANGE repl:zset 0 -1 WITHSCORES

批量操作测试

使用管道模式

# 创建测试数据文件
echo -e "SET key1 value1\nSET key2 value2\nSET key3 value3" > test-commands.txt
​
# 通过管道执行
redis-cli -p 6388 --pipe < test-commands.txt

性能测试

# 基本性能测试
redis-benchmark -h 127.0.0.1 -p 6388 -n 10000 -c 50
​
# 测试 SET 操作
redis-benchmark -h 127.0.0.1 -p 6388 -t set -n 10000 -d 100
​
# 测试 GET 操作  
redis-benchmark -h 127.0.0.1 -p 6388 -t get -n 10000

第三章:RESP协议实现

第四章:网络编程与事件循环

第五章:KV存储引擎实现

第六章:持久化实现

第七章:主从复制实现

这几个章节需要搭配源码一起看,详细的学习文档和源码都在这个视频讲解中给出来,大家可以去观看领取

****************************-**********

第八章:性能优化与调优

8.1 性能测试基准

使用redis-benchmark进行性能测试,对比优化前后的效果:

# 基准测试命令
redis-benchmark -h 127.0.0.1 -p 6379 -n 100000 -c 50 -P 10 -t set,get
redis-benchmark -h 127.0.0.1 -p 6379 -n 50000 -c 10 -P 1 -d 1000 -t set,get

8.1.1 性能优化对比

8.2 关键优化技术

8.2.1 AOF性能优化核心

最关键的优化是AOF的异步写入机制:

// 优化前:同步写入(性能杀手)
void appendAOF_slow(const std::string& cmd) {
    std::ofstream file(aof_path_, std::ios::app);
    file << cmd;
    file.flush();  // 立即刷盘,QPS暴跌
}
​
// 优化后:异步批量写入
void appendAOF_fast(const std::string& cmd) {
    {
        std::lock_guard<std::mutex> lock(queue_mutex_);
        aof_queue_.push(cmd);
        pending_bytes_ += cmd.size();
    }
    cv_.notify_one();  // 唤醒后台写入线程
}

8.2.2 网络I/O优化

// 优化技巧1:TCP_NODELAY避免Nagle算法延迟
int opt = 1;
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
​
// 优化技巧2:writev批量发送
struct iovec iov[64];  // 增大到64个iovec
int count = 0;
for (const auto& chunk : out_chunks) {
    iov[count].iov_base = (void*)chunk.data();
    iov[count].iov_len = chunk.size();
    if (++count >= 64) break;
}
writev(fd, iov, count);
​
// 优化技巧3:边沿触发一次性读完
while (true) {
    ssize_t n = read(fd, buf, sizeof(buf));
    if (n <= 0) {
        if (errno == EAGAIN) break;  // 读完了
        // 处理错误
    }
    process_data(buf, n);
}

8.3 内存与算法优化

8.3.1 ZSet自适应存储

// 小集合用vector,大集合用跳表
class ZSetRecord {
    static constexpr size_t THRESHOLD = 128;
    
    std::vector<std::pair<double, std::string>> small_set;  // <128元素
    std::unique_ptr<Skiplist> skiplist;                     // >=128元素
    
    void checkAndConvert() {
        if (!use_skiplist && small_set.size() >= THRESHOLD) {
            convertToSkiplist();  // 自动升级
        }
    }
};

8.3.2 预分配和对象复用

// 预分配AOF文件空间,避免频繁扩展
void preallocateAOF(int fd, size_t size) {
    if (posix_fallocate(fd, 0, size) == 0) {
        printf("Preallocated %zu bytes for AOF\n", size);
    }
}
​
// 连接对象复用
class ConnectionPool {
    std::vector<std::unique_ptr<Conn>> free_conns_;
    
    std::unique_ptr<Conn> acquire() {
        if (!free_conns_.empty()) {
            auto conn = std::move(free_conns_.back());
            free_conns_.pop_back();
            conn->reset();  // 重置状态
            return conn;
        }
        return std::make_unique<Conn>();
    }
};

结语

技术重点

  • 高性能:异步AOF、批量I/O、边沿触发
  • 完整架构:网络层、协议层、存储层分离
  • 数据结构:跳表、自适应ZSet、Hash表
  • 持久化:AOF+RDB双重保障
  • 复制:主从同步、部分重同步
  • 优化:从1.4k到55k QPS的性能提升(这里是针对SET),如果是GET可以媲美Redis.

学习价值

  • 系统设计能力:理解高性能服务器架构
  • 网络编程技能:掌握epoll、非阻塞I/O
  • 存储系统知识:学会设计持久化方案
  • 性能调优经验:掌握常见优化技巧
  • 面试准备:涵盖Redis相关高频面试题
#我的成功项目解析##校招##秋招##c++##牛客创作赏金赛#
全部评论
坐标南京,OD岗位多多
点赞 回复 分享
发布于 08-28 20:51 贵州

相关推荐

评论
1
5
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务