代码库概览

1. 概述

本项目实现了一个受 Redis 启发的轻量级内存键值存储。它支持基本操作,如 GET、SET、DEL 和 EXPIRE,并使用追加只文件(AOF)实现了简单的持久化机制。

2. 核心数据结构

Database

中央内存数据存储。它使用哈希映射(std::unordered_map<std::string, Value>)来存储键和值。每个 Value 是一个标记联合(tagged union),可以表示字符串、列表、哈希或集合。

AOFWriter

处理追加只文件持久化。每个写操作(SET、DEL、EXPIRE)都被序列化为一条命令并追加到 AOF 文件中。写入器使用缓冲流以最小化系统调用。

Parser

一个简单的 RESP(Redis 序列化协议)解析器。它从客户端套接字读取原始字节,并将其转换为结构化的命令对象(例如 Command::Type::GET、Command::Type::SET)。

Connection

表示一个客户端 TCP 连接。它拥有套接字、一个读取缓冲区和一个写入缓冲区。它与 Parser 交互以解码请求,并与 Database 交互以处理这些请求。

Server

主入口。它 TCP 端口,接受新连接,并管理事件循环。它使用非阻塞套接字和基于 epoll 的事件循环来处理数千个并发连接。

3. 主流程

  1. 启动:Server 绑定到端口 6379 并进入事件循环。
  2. 请求解析:当客户端发送数据时,Connection 将其读取到其缓冲区中。Parser 将 RESP 协议解码为一条 Command。
  3. 命令执行:Command 被分派到 Database。例如,GET 命令返回给定键的值,而 SET 命令更新键值对并写入到 AOFWriter。
  4. 持久化:每个修改命令都被追加到 AOF 文件中。在重启时,服务器重放 AOF 文件以恢复状态。
  5. 响应写入:命令的结果被序列化回 RESP 格式,并在客户端套接字变为可写时写入到客户端套接字。

4. 关键设计决策

  • 单线程事件循环:像 Redis 一样,服务器使用单线程事件循环来保证操作简单性和原子性。
  • 非阻塞 I/O:套接字设置为非阻塞模式,允许事件循环在没有线程的情况下处理许多连接。
  • 值的标记联合:使用标记联合可以避免小值的堆分配,并使类型检查更加明确。
  • AOF 持久化:我们使用追加只日志而不是快照来保证持久性。这确保了在崩溃情况下最多丢失一秒的数据。