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 请求处理流程
一个完整的请求经历以下阶段:
- 网络接收: epoll监听客户端连接和数据
- 协议解析: 解析RESP格式的命令
- 命令执行: 在KV存储中执行操作
- 持久化: AOF记录命令,RDB定期快照
- 主从复制: 同步命令到从节点
- 响应返回: 将结果序列化为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相关高频面试题