第二部分:项目结构与构建系统
2.1 目录结构规划
企业级项目必须有清晰、规范的目录结构,便于团队协作和长期维护。
c-cache/
├── CMakeLists.txt # 顶层CMake配置
├── README.md # 项目说明
├── LICENSE # 许可证
├── .gitignore # Git忽略文件
├── .clang-format # 代码格式化配置
├── .clang-tidy # 静态分析配置
│
├── src/ # 源代码目录
│ ├── main.c # 程序入口
│ ├── server/ # 服务器核心
│ │ ├── event_loop.h
│ │ ├── event_loop.c # 事件循环(epoll封装)
│ │ ├── connection.h
│ │ ├── connection.c # TCP连接管理
│ │ ├── acceptor.h
│ │ └── acceptor.c # 监听器
│ │
│ ├── protocol/ # 协议处理
│ │ ├── resp.h
│ │ ├── resp.c # RESP协议解析
│ │ ├── command.h
│ │ └── command.c # 命令分发与执行
│ │
│ ├── datastore/ # 数据存储
│ │ ├── dict.h
│ │ ├── dict.c # 哈希表实现
│ │ ├── sds.h
│ │ ├── sds.c # 动态字符串
│ │ ├── object.h
│ │ ├── object.c # 数据对象(string/hash/list)
│ │ ├── expire.h
│ │ └── expire.c # 过期键管理
│ │
│ ├── persistence/ # 持久化
│ │ ├── rdb.h
│ │ ├── rdb.c # RDB快照
│ │ ├── aof.h
│ │ └── aof.c # AOF日志
│ │
│ ├── replication/ # 主从复制
│ │ ├── replication.h
│ │ └── replication.c # 复制逻辑
│ │
│ ├── utils/ # 工具函数
│ │ ├── alloc.h
│ │ ├── alloc.c # 内存分配器封装
│ │ ├── log.h
│ │ ├── log.c # 日志系统
│ │ ├── config.h
│ │ ├── config.c # 配置解析
│ │ ├── time.h
│ │ └── time.c # 时间工具
│ │
│ └── include/ # 公共头文件
│ ├── version.h # 版本定义
│ └── error.h # 错误码定义
│
├── tests/ # 测试目录
│ ├── unit/ # 单元测试
│ │ ├── test_dict.c
│ │ ├── test_sds.c
│ │ └── test_resp.c
│ ├── integration/ # 集成测试
│ │ └── test_server.c
│ └── benchmark/ # 性能测试
│ └── bench_cache.c
│
├── deps/ # 第三方依赖
│ ├── libevent/ # libevent源码(可选)
│ └── jemalloc/ # jemalloc源码(可选)
│
├── config/ # 配置文件
│ ├── ccache.conf # 默认配置
│ └── ccache.example.conf # 配置示例
│
├── scripts/ # 脚本工具
│ ├── build.sh # 构建脚本
│ ├── run.sh # 运行脚本
│ ├── benchmark.sh # 性能测试脚本
│ └── cluster_setup.sh # 集群部署脚本
│
└── docs/ # 文档
├── architecture.md # 架构设计
├── api.md # API文档
└── deployment.md # 部署指南
2.2 CMake构建配置
企业级项目需要可靠的构建系统。CMake是C/C++项目的事实标准。
# CMakeLists.txt - 顶层配置
cmake_minimum_required(VERSION 3.15)
project(CCache VERSION 1.0.0 LANGUAGES C)
# ========== 全局配置 ==========
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
# 设置编译选项
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-g -O0 -Wall -Wextra -Werror -Wno-unused-parameter)
add_compile_options(-fsanitize=address -fsanitize=undefined)
add_link_options(-fsanitize=address -fsanitize=undefined)
else()
add_compile_options(-O3 -DNDEBUG -flto -march=native)
add_link_options(-flto)
endif()
# 平台检测
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_compile_options(-D__LINUX__)
set(PLATFORM_LIBS pthread rt)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_compile_options(-D__MACOS__)
set(PLATFORM_LIBS pthread)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_compile_options(-D__WINDOWS__)
set(PLATFORM_LIBS ws2_32)
endif()
# ========== 第三方依赖 ==========
# 查找libevent(可选,也可自己实现epoll封装)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBEVENT REQUIRED libevent)
# 查找jemalloc(可选)
find_package(jemalloc)
if(jemalloc_FOUND)
add_compile_options(-DUSE_JEMALLOC)
set(EXTRA_LIBS ${EXTRA_LIBS} jemalloc::jemalloc)
endif()
# ========== 源代码组织 ==========
# 核心模块
set(CORE_SOURCES
src/main.c
src/server/event_loop.c
src/server/connection.c
src/server/acceptor.c
src/protocol/resp.c
src/protocol/command.c
src/datastore/dict.c
src/datastore/sds.c
src/datastore/object.c
src/datastore/expire.c
src/persistence/rdb.c
src/persistence/aof.c
src/replication/replication.c
src/utils/alloc.c
src/utils/log.c
src/utils/config.c
src/utils/time.c
)
# 公共头文件目录
include_directories(
src/include
src
${LIBEVENT_INCLUDE_DIRS}
)
# 生成可执行文件
add_executable(ccache ${CORE_SOURCES})
# 链接库
target_link_libraries(ccache
${LIBEVENT_LIBRARIES}
${PLATFORM_LIBS}
${EXTRA_LIBS}
)
# ========== 安装配置 ==========
install(TARGETS ccache DESTINATION bin)
install(FILES config/ccache.conf DESTINATION etc)
install(DIRECTORY scripts/ DESTINATION bin/scripts)
# ========== 测试 ==========
enable_testing()
add_subdirectory(tests)
# ========== 打包配置 ==========
set(CPACK_GENERATOR "TGZ;ZIP")
set(CPACK_PACKAGE_NAME "ccache")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
include(CPack)
第三部分:核心数据结构实现
3.1 SDS(Simple Dynamic String)实现
C语言字符串的痛点:获取长度需要O(n)遍历、缓冲区溢出风险、二进制不安全。SDS是Redis采用的高性能字符串实现,我们将其作为C-Cache的基础组件。
// src/datastore/sds.h
#ifndef CCACHE_SDS_H
#define CCACHE_SDS_H
#include <stddef.h>
#include <stdint.h>
/* SD S头部结构(5种类型,根据字符串长度选择) */
#define SDS_TYPE_5 0 // 长度 < 32
#define SDS_TYPE_8 1 // 长度 < 256
#define SDS_TYPE_16 2 // 长度 < 64K
#define SDS_TYPE_32 3 // 长度 < 4GB
#define SDS_TYPE_64 4 // 长度 < 2^63
/* 内部使用的SDS头(内存布局:len + alloc + flags + buf[]) */
struct __attribute__((__packed__)) sdshdr8 {
uint8_t len; // 已使用长度
uint8_t alloc; // 分配的总长度(不含header和'\0')
unsigned char flags; // 类型标识(低3位)
char buf[];
};
struct __attribute__((__packed__)) sdshdr16 {
uint16_t len;
uint16_t alloc;
unsigned char flags;
char buf[];
};
struct __attribute__((__packed__)) sdshdr32 {
uint32_t len;
uint32_t alloc;
unsigned char flags;
char buf[];
};
struct __attribute__((__packed__)) sdshdr64 {
uint64_t len;
uint64_t alloc;
unsigned char flags;
char buf[];
};
/* SDS类型定义 */
typedef char* sds;
/* 基础操作API */
sds sdsnew(const char *init);
sds sdsnewlen(const void *init, size_t initlen);
sds sdsdup(const sds s);
void sdsfree(sds s);
size_t sdslen(const sds s);
size_t sdsavail(const sds s);
sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t);
sds sdscpylen(sds s, const char *t, size_t len);
sds sdscpy(sds s, const char *t);
sds sdsgrowzero(sds s, size_t len);
sds sdscatprintf(sds s, const char *fmt, ...);
void sdstrim(sds s, const char *cset);
void sdsrange(sds s, int start, int end);
int sdscmp(const sds s1, const sds s2);
sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
void sdsfreesplitres(sds *tokens, int count);
void sdstolower(sds s);
void sdstoupper(sds s);
sds sdsfromlonglong(long long value);
#endif // CCACHE_SDS_H
j// src/datastore/sds.c
#include "sds.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
/* 获取SDS头指针 */
static inline struct sdshdr8 *sdsheader8(const sds s) {
return (struct sdshdr8 *)(s - sizeof(struct sdshdr8));
}
static inline struct sdshdr16 *sdsheader16(const sds s) {
return (struct sdshdr16 *)(s - sizeof(struct sdshdr16));
}
static inline struct sdshdr32 *sdsheader32(const sds s) {
return (struct sdshdr32 *)(s - sizeof(struct sdshdr32));
}
static inline struct sdshdr64 *sdsheader64(const sds s) {
return (struct sdshdr64 *)(s - sizeof(struct sdshdr64));
}
/* 获取SDS类型 */
static inline char sdsflags(const sds s) {
return s[-1]; // flags在buf前面一个字节
}
/* 获取SDS长度 - O(1)时间复杂度!*/
size_t sdslen(const sds s) {
if (!s) return 0;
unsigned char flags = sdsflags(s);
switch(flags & SDS_TYPE_MASK) {
case SDS_TYPE_5: return (size_t)((struct sdshdr5 *)s->buf)[-1] >> 3;
case SDS_TYPE_8: return sdsheader8(s)->len;
case SDS_TYPE_16: return sdsheader16(s)->len;
case SDS_TYPE_32: return sdsheader32(s)->len;
case SDS_TYPE_64: return sdsheader64(s)->len;
default: return 0;
}
}
/* 获取可用空间 */
size_t sdsavail(const sds s) {
if (!s) return 0;
unsigned char flags = sdsflags(s);
switch(flags & SDS_TYPE_MASK) {
case SDS_TYPE_5: return 0;
case SDS_TYPE_8: return sdsheader8(s)->alloc - sdsheader8(s)->len;
case SDS_TYPE_16: return sdsheader16(s)->alloc - sdsheader16(s)->len;
case SDS_TYPE_32: return sdsheader32(s)->alloc - sdsheader32(s)->len;
case SDS_TYPE_64: return sdsheader64(s)->alloc - sdsheader64(s)->len;
default: return 0;
}
}
/* 创建新SDS(从C字符串) */
sds sdsnew(const char *init) {
return sdsnewlen(init, init ? strlen(init) : 0);
}
/* 创建新SDS(从二进制数据) */
sds sdsnewlen(const void *init, size_t initlen) {
sds s;
struct sdshdr8 *sh;
size_t hdrlen;
unsigned char type = sdsReqType(initlen);
hdrlen = sdsHdrSize(type);
s = malloc(hdrlen + initlen + 1);
if (!s) return NULL;
sh = (struct sdshdr8 *)s;
s = (char *)s + hdrlen;
switch(type) {
case SDS_TYPE_5:
*((uint8_t *)sh) = (uint8_t)((initlen << 3) | SDS_TYPE_5);
break;
case SDS_TYPE_8:
sh->len = initlen;
sh->alloc = initlen;
sh->flags = type;
break;
// ... 其他类型类似
}
if (initlen && init) {
memcpy(s, init, initlen);
}
s[initlen] = '\0';
return s;
}
/* 追加字符串(核心优化:预分配空间,减少内存分配次数) */
sds sdscatlen(sds s, const void *t, size_t len) {
size_t curlen = sdslen(s);
s = sdsMakeRoomFor(s, len);
if (!s) return NULL;
memcpy(s + curlen, t, len);
s[curlen + len] = '\0';
sdslen(s) = curlen + len;
return s;
}
/* 空间预分配策略(Redis经典策略) */
sds sdsMakeRoomFor(sds s, size_t addlen) {
size_t avail = sdsavail(s);
if (avail >= addlen) return s;
size_t newlen = sdslen(s) + addlen;
size_t hdrlen = sdsHdrSize(sdsflags(s));
char *newsh;
/* 核心策略:小于1MB时翻倍扩容,大于1MB时每次增加1MB */
if (newlen < SDS_MAX_PREALLOC) {
newlen *= 2;
} else {
newlen += SDS_MAX_PREALLOC;
}
unsigned char newtype = sdsReqType(newlen);
if (newtype == SDS_TYPE_5) newtype = SDS_TYPE_8;
size_t newhdrlen = sdsHdrSize(newtype);
if (newtype == sdsflags(s)) {
newsh = realloc(s - hdrlen, newhdrlen + newlen + 1);
if (!newsh) return NULL;
s = newsh + hdrlen;
} else {
// 类型变化,需要重新分配并拷贝
newsh = malloc(newhdrlen + newlen + 1);
if (!newsh) return NULL;
memcpy(newsh + newhdrlen, s, curlen + 1);
free(s - hdrlen);
s = newsh + newhdrlen;
}
// 更新头部信息
// ...
return s;
}
3.2 高性能哈希表实现
哈希表是缓存系统的核心数据结构。C-Cache实现了渐进式哈希表,解决单次扩容导致的服务卡顿问题。
// src/datastore/dict.h
#ifndef CCACHE_DICT_H
#define CCACHE_DICT_H
#include <stdint.h>
#include "sds.h"
/* 哈希函数类型 */
typedef uint64_t (*dictHashFunction)(const void *key, size_t len);
/* 键值对 */
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
struct dictEntry *next; // 链地址法解决冲突
} dictEntry;
/* 字典类型(多态操作) */
typedef struct dictType {
uint64_t (*hashFunction)(const void *key, size_t len);
void *(*keyDup)(void *privdata, const void *key);
void *(*valDup)(void *privdata, const void *obj);
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
void (*keyDestructor)(void *privdata, void *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType;
/* 哈希表(一个字典有两个哈希表,用于渐进式rehash) */
typedef struct dictht {
dictEntry **table; // 哈希桶数组
unsigned long size; // 哈希表大小
unsigned long sizemask; // 用于计算索引的掩码(size-1)
unsigned long used; // 已使用节点数
} dictht;
/* 字典主结构 */
typedef struct dict {
dictType *type; // 类型特定函数
void *privdata; // 私有数据
dictht ht[2]; // 两个哈希表(ht[1]用于rehash)
int rehashidx; // rehash进度(-1表示未进行)
int iterators; // 当前运行的迭代器数量
} dict;
/* 迭代器 */
typedef struct dictIterator {
dict *d;
int table; // 当前遍历的哈希表索引(0或1)
int index; // 当前桶索引
int safe; // 是否安全迭代器(允许修改)
dictEntry *entry; // 当前节点
dictEntry *nextEntry; // 下一个节点
} dictIterator;
/* API声明 */
dict *dictCreate(dictType *type, void *privdata);
int dictExpand(dict *d, unsigned long size);
int dictRehash(dict *d, int n);
int dictAdd(dict *d, void *key, void *val);
dictEntry *dictFind(dict *d, const void *key);
void *dictFetchValue(dict *d, const void *key);
int dictDelete(dict *d, const void *key);
void dictRelease(dict *d);
dictIterator *dictGetIterator(dict *d);
dictEntry *dictNext(dictIterator *iter);
void dictReleaseIterator(dictIterator *iter);
#endif
j// src/datastore/dict.c - 核心实现
#include "dict.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
/* 初始哈希表大小 */
#define DICT_HT_INITIAL_SIZE 4
/* 哈希表扩容因子(used >= size时触发扩容)*/
#define DICT_HT_EXPAND_FACTOR 1
/* 缩容因子(used < size * 0.1时触发缩容)*/
#define DICT_HT_SHRINK_FACTOR 10
/* 创建字典 */
dict *dictCreate(dictType *type, void *privdata) {
dict *d = malloc(sizeof(dict));
if (!d) return NULL;
d->type = type;
d->privdata = privdata;
d->rehashidx = -1;
d->iterators = 0;
d->ht[0] = (dictht){ NULL, 0, 0, 0 };
d->ht[1] = (dictht){ NULL, 0, 0, 0 };
if (dictExpand(d, DICT_HT_INITIAL_SIZE) != 0) {
free(d);
return NULL;
}
return d;
}
/* 扩容/缩容核心函数 */
int dictExpand(dict *d, unsigned long size) {
// 如果正在rehash,不能再次扩容
if (d->rehashidx != -1) return -1;
// 确保size是2的幂
unsigned long realsize = DICT_HT_INITIAL_SIZE;
while (realsize < size) realsize <<= 1;
// 创建新哈希表
dictht n = { NULL, realsize, realsize - 1, 0 };
n.table = calloc(realsize, sizeof(dictEntry*));
if (!n.table) return -1;
// 如果当前哈希表为空,直接使用新表
if (d->ht[0].table == NULL) {
d->ht[0] = n;
return 0;
}
// 否则设置ht[1]为新表,启动渐进式rehash
d->ht[1] = n;
d->rehashidx = 0;
return 0;
}
/* 渐进式rehash(核心:分步迁移,避免单次卡顿)*/
int dictRehash(dict *d, int n) {
// 空转保护
if (d->rehashidx == -1) return 0;
int empty_visits = n * 10; // 最多跳过10*n个空桶
while (n-- && d->ht[0].used != 0) {
// 跳过空桶(最多跳过empty_visits次)
while (d->ht[0].table[d->rehashidx] == NULL) {
d->rehashidx++;
if (--empty_visits == 0) return 1;
}
// 迁移当前桶的所有节点
dictEntry *de = d->ht[0].table[d->rehashidx];
while (de) {
dictEntry *next = de->next;
// 计算在新表中的索引
unsigned long h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = next;
}
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++;
}
// 检查rehash是否完成
if (d->ht[0].used == 0) {
free(d->ht[0].table);
d->ht[0] = d->ht[1];
d->ht[1] = (dictht){ NULL, 0, 0, 0 };
d->rehashidx = -1;
return 0;
}
return 1;
}
/* 在rehash过程中查找键 */
dictEntry *dictFind(dict *d, const void *key) {
// 如果正在rehash,执行一步rehash(每次查找都推进)
if (d->rehashidx != -1) {
dictRehash(d, 1);
}
unsigned long h = dictHashKey(d, key);
// 先在ht[0]查找
dictEntry *de = d->ht[0].table[h & d->ht[0].sizemask];
while (de) {
if (dictCompareKeys(d, key, de->key)) return de;
de = de->next;
}
// 如果正在rehash,再到ht[1]查找
if (d->rehashidx != -1) {
de = d->ht[1].table[h & d->ht[1].sizemask];
while (de) {
if (dictCompareKeys(d, key, de->key)) return de;
de = de->next;
}
}
return NULL;
}
/* 添加键值对 */
int dictAdd(dict *d, void *key, void *val) {
// 检查是否需要扩容
if (d->ht[0].used >= d->ht[0].size && d->rehashidx == -1) {
dictExpand(d, d->ht[0].used * 2);
}
// 如果正在rehash,推进
if (d->rehashidx != -1) dictRehash(d, 1);
// 计算哈希值
unsigned long h = dictHashKey(d, key);
int table = d->rehashidx != -1 ? 1 : 0;
unsigned long idx = h & d->ht[table].sizemask;
// 检查键是否已存在
dictEntry *de = d->ht[table].table[idx];
while (de) {
if (dictCompareKeys(d, key, de->key)) return -1;
de = de->next;
}
// 创建新节点
de = malloc(sizeof(dictEntry));
if (!de) return -1;
de->key = dictDupKey(d, key);
de->v.val = dictDupVal(d, val);
de->next = d->ht[table].table[idx];
d->ht[table].table[idx] = de;
d->ht[table].used++;
return 0;
}
3.3 内存分配器封装
// src/utils/alloc.h
#ifndef CCACHE_ALLOC_H
#define CCACHE_ALLOC_H
#include <stddef.h>
/* 内存分配器接口(支持jemalloc、tcmalloc或系统malloc)*/
void *zmalloc(size_t size);
void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);
void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
void zmalloc_enable_thread_safeness(void);
#endif
// src/utils/alloc.c
#include "alloc.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#ifdef USE_JEMALLOC
#include <jemalloc/jemalloc.h>
#define malloc je_malloc
#define calloc je_calloc
#define realloc je_realloc
#define free je_free
#endif
/* 内存使用统计(原子操作) */
static size_t used_memory = 0;
static pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
/* OOM处理函数 */
static void (*oom_handler)(size_t) = NULL;
void *zmalloc(size_t size) {
void *ptr = malloc(size);
if (!ptr) {
if (oom_handler) oom_handler(size);
return NULL;
}
pthread_mutex_lock(&used_memory_mutex);
used_memory += size;
pthread_mutex_unlock(&used_memory_mutex);
return ptr;
}
void *zcalloc(size_t size) {
void *ptr = calloc(1, size);
if (!ptr) {
if (oom_handler) oom_handler(size);
return NULL;
}
pthread_mutex_lock(&used_memory_mutex);
used_memory += size;
pthread_mutex_unlock(&used_memory_mutex);
return ptr;
}
void *zrealloc(void *ptr, size_t size) {
size_t old_size = ptr ? malloc_usable_size(ptr) : 0;
void *newptr = realloc(ptr, size);
if (!newptr && size > 0) {
if (oom_handler) oom_handler(size);
return NULL;
}
pthread_mutex_lock(&used_memory_mutex);
used_memory -= old_size;
used_memory += size;
pthread_mutex_unlock(&used_memory_mutex);
return newptr;
}
void zfree(void *ptr) {
if (!ptr) return;
size_t old_size = malloc_usable_size(ptr);
pthread_mutex_lock(&used_memory_mutex);
used_memory -= old_size;
pthread_mutex_unlock(&used_memory_mutex);
free(ptr);
}
size_t zmalloc_used_memory(void) {
size_t mem;
pthread_mutex_lock(&used_memory_mutex);
mem = used_memory;
pthread_mutex_unlock(&used_memory_mutex);
return mem;
}