目录
一、KV 存储的核心设计哲学
1. 键值对的本质
KV 存储的核心是将数据抽象为键值对(Key-Value Pair)。键作为唯一标识符,值则承载任意格式的数据。这种设计摒弃了传统关系型数据库的复杂表结构,将数据存储简化为 “键→值” 的映射,从而实现了 O (1) 级别的读写效率。
2. 存储引擎的黑魔法
主流 KV 存储(如 Redis、RocksDB)采用哈希表或跳表等数据结构,结合内存与磁盘分层存储:
内存型(如 Redis):将数据常驻内存,通过异步持久化保证可靠性。
持久化型(如 LevelDB):数据写入磁盘,利用 LSM-Tree 结构优化读写性能。
3. 分布式扩展的奥秘
分布式 KV 存储(如 DynamoDB)通过一致性哈希、分片(Sharding)和复制(Replication)实现水平扩展:
分片:将数据分散到多个节点,避免单点瓶颈。
复制:每个分片保留多个副本,确保高可用性。
二、KV 存储的核心模块
1.reactor/ntyco/io_uring
2.协议层kv-store
3.数据结构rbtree/array/hash
4.内存池mp_pool
5.测试用例test_case
三、部分源码细究
#include"kvstore.h"
#define KVSTORE_MAX_TOKEN 128
const char*commands[]={
"SET","GET","DEL","MOD","COUNT",
"RSET","RGET","RDEL","RMOD","RCOUNT",
"HSET","HGET","HDEL","HMOD","HCOUNT",
};
enum{
KV_CMD_START= 0,
KV_CMD_SET=KV_CMD_START,
KV_CMD_GET,
KV_CMD_DEL,
KV_CMD_MOD,
KV_CMD_COUNT,
KV_CMD_RSET,
KV_CMD_RGET,
KV_CMD_RDEL,
KV_CMD_RMOD,
KV_CMD_RCOUNT,
KV_CMD_HSET,
KV_CMD_HGET,
KV_CMD_HDEL,
KV_CMD_HMOD,
KV_CMD_HCOUNT,
KV_CMD_SIZE,
};
void *kvstore_malloc(size_t size){
#if ENABLE_MEM_POOL
char *ptr = mp_allock(&M);
#else
return malloc(size);
#endif
}
void kvstore_free(void *ptr){
#if ENABLE_MEM_POOL
#else
return free(ptr);
#endif
}
#if ENABLE_RBTREE_KVENGINE
int kvstore_rbtree_set(char *key, char *value){
return kvs_rbtree_set(&Tree, key, value);
}
char* kvstore_rbtree_get( char *key){
return kvs_rbtree_get(&Tree,key);
}
int kvstore_rbtree_delete( char *key){
return kvs_rbtree_delete(&Tree,key);
}
int kvstore_rbtree_modify( char *key, char *value){
return kvs_rbtree_modify(&Tree,key,value);
}
int kvstore_rbtree_count(){
return kvs_rbtree_count(&Tree);
}
#endif
#if ENABLE_ARRAY_KVENGINE
int kvstore_array_set(char *key,char *value){
return kvs_array_set(&Array,key,value);
}
char *kvstore_array_get(char*key){
return kvs_array_get(&Array,key);
}
int kvstore_array_del(char*key){
return kvs_array_del(&Array,key);
}
int kvstore_array_mod(char*key,char*value){
return kvs_array_mod(&Array,key,value);
}
int kvstore_array_count(){
return kvs_array_count(&Array);
}
#endif
#if ENABLE_HASH_KVENGINE
int kvstore_hash_set(char *key,char *value){
return kvs_hash_set(&Hash,key,value);
}
char *kvstore_hash_get(char*key){
return kvs_hash_get(&Hash,key);
}
int kvstore_hash_del(char*key){
return kvs_hash_delete(&Hash,key);
}
int kvstore_hash_mod(char*key,char*value){
return kvs_hash_modify(&Hash,key,value);
}
int kvstore_hash_count(){
return kvs_hash_count(&Hash);
}
#endif
int kvstore_split_token(char*msg,char**tokens){
if(msg==NULL || tokens==NULL) return -1;
int idx = 0;
char *token = strtok(msg," ");
while(token!=NULL){
tokens[idx++]=token;
token = strtok(NULL," ");
}
return idx;
}
int kvstore_parser_protocol(struct conn_item*item,char**tokens,int count){
if(item==NULL||tokens[0]==NULL||count==0) return -1;
int cmd = KV_CMD_START;
for(cmd=KV_CMD_START;cmd<KV_CMD_SIZE;cmd++){
if(strcmp(commands[cmd],tokens[0])==0) break;
}
char*msg=item->wbuffer;
char*key = tokens[1];
char*value =tokens[2];
memset(msg,0,BUFFER_LENGTH);
switch(cmd){
case KV_CMD_SET:{
int res=kvstore_array_set(key,value);
if(res==0){
snprintf(msg,BUFFER_LENGTH,"SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"FAILED");
}
break;
}
case KV_CMD_GET:{
char *values=kvstore_array_get(key);
if(values!=NULL){
snprintf(msg,BUFFER_LENGTH,"%s",values);
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
//printf("get:%s\n",values);
break;
}
case KV_CMD_DEL:{
//printf("del\n");
int res =kvstore_array_del(key);
if(res<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else if(res==0){
snprintf(msg,BUFFER_LENGTH,"%s","SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
break;
}
case KV_CMD_MOD:{
//printf("mod\n");
int res =kvstore_array_mod(key,value);
if(res<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else if(res==0){
snprintf(msg,BUFFER_LENGTH,"%s","SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
break;
}
case KV_CMD_COUNT:{
int count =kvs_array_count(&Array);
if(count<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else{
snprintf(msg,BUFFER_LENGTH,"%d",count);
}
break;
}
case KV_CMD_RSET:{
int res=kvstore_rbtree_set(key,value);
if(res==0){
snprintf(msg,BUFFER_LENGTH,"SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"FAILED");
}
break;
}
case KV_CMD_RGET:{
char*values=kvstore_rbtree_get(key);
if(values!=NULL){
snprintf(msg,BUFFER_LENGTH,"%s",values);
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
break;
}
case KV_CMD_RDEL:{
int res =kvstore_rbtree_delete(key);
if(res<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else if(res==0){
snprintf(msg,BUFFER_LENGTH,"%s","SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
break;
}
case KV_CMD_RMOD:{
int res=kvstore_rbtree_modify(key,value);
if(res<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else if(res==0){
snprintf(msg,BUFFER_LENGTH,"%s","SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
break;
}
case KV_CMD_RCOUNT:{
int count =kvstore_rbtree_count();
if(count<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else{
snprintf(msg,BUFFER_LENGTH,"%d",count);
}
break;
}
case KV_CMD_HSET:{
int res=kvstore_hash_set(key,value);
if(res==0){
snprintf(msg,BUFFER_LENGTH,"SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"FAILED");
}
break;
}
case KV_CMD_HGET:{
char*values=kvstore_hash_get(key);
if(values!=NULL){
snprintf(msg,BUFFER_LENGTH,"%s",values);
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
break;
}
case KV_CMD_HDEL:{
int res =kvstore_hash_del(key);
if(res<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else if(res==0){
snprintf(msg,BUFFER_LENGTH,"%s","SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
break;
}
case KV_CMD_HMOD:{
int res=kvstore_hash_mod(key,value);
if(res<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else if(res==0){
snprintf(msg,BUFFER_LENGTH,"%s","SUCCESS");
}else{
snprintf(msg,BUFFER_LENGTH,"NO EXIST");
}
break;
}
case KV_CMD_HCOUNT:{
int count =kvstore_hash_count();
if(count<0){
snprintf(msg,BUFFER_LENGTH,"%s","ERROR");
}else{
snprintf(msg,BUFFER_LENGTH,"%d",count);
}
break;
}
default:{
//printf("cmd%s\n",commands[cmd]);
assert(0);
}
}
}
int kvstore_requese(struct conn_item*item){
//printf("recv: %s\n",item->rbuffer);
char * msg = item->rbuffer;
char *tockens[KVSTORE_MAX_TOKEN];
int count = kvstore_split_token(msg,tockens);
int idx;
for(idx =0;idx<count;idx++){
//printf("idx:%s\n",tockens[idx]);
}
kvstore_parser_protocol(item,tockens,count);
return 0;
}
int init_kvengine(void){
#if ENABLE_ARRAY_KVENGINE
kvstore_array_create(&Array);
#endif
#if ENABLE_RBTREE_KVENGINE
kvstore_rbtree_create(&Tree);
#endif
#if ENABLE_HASH_KVENGINE
kvstore_hash_create(&Hash);
#endif
}
int exit_kvengine(void){
#if ENABLE_ARRAY_KVENGINE
kvstore_array_destory(&Array);
#endif
#if ENABLE_RBTREE_KVENGINE
kvstore_rbtree_destory(&Tree);
#endif
#if ENABLE_HASH_KVENGINE
kvstore_hash_destory(&Hash);
#endif
}
int init_pool(void){
#if ENABLE_MEM_POOL
mp_init(&M,4096);
#endif
}
int destory_pool(void){
#if ENABLE_MEM_POOL
mp_dest(&M);
#endif
}
int main(){
init_kvengine();
init_pool();
#if (ENABLE_NETWORK_SELECT==NETWORK_EPOLL)
epoll_entry();
#elif(ENABLE_NETWORK_SELECT==NETWORK_NTYCO)
ntyco_enrty();
#elif(ENABLE_NETWORK_SELECT==NETWORK_IOURING)
#endif
exit_kvengine();
destory_pool();
}
这些是对外用户封装的接口
①设置命令对应
②开辟内存和释放内存的接口 可以兼容内存池
③对接红黑叔 数组 哈希的接口
④分割函数strtok的使用
⑤制定协议的接口
⑥接收数据的接口
⑦选择数据结构的接口
⑧释放资源的接口
⑨定义和销毁内存池的接口
⑩main函数一切的起点
内存池的介绍和复习
#include<stdlib.h>
#include<stdio.h>
#include"kvstore.h"
#define MEM_PAGE_SIZE 4096
typedef struct mempool_s{
int block_size;
int free_count;
char*free_ptr;
char*mem;
}mempool_t;
//内存池的初始化
int mp_init(mempool_t*m,int size){
if(m==NULL) return -1;
if(size<16) size=16;
m->block_size=size;//固定大小的内存块
m->mem=(char*)malloc(MEM_PAGE_SIZE);
if(m->mem==NULL) return -2;
m->free_ptr=m->mem;
m->free_count=MEM_PAGE_SIZE/size;
int i =0;
char*ptr= m->free_ptr;
for(i;i<m->free_count;i++){
*(char**)ptr=ptr+size;//☆
ptr+=size;
}
*(char**)ptr=NULL;
}
//内存池的销毁
void mp_dest(mempool_t*m){
if(m==NULL||m->mem==NULL) return;
free(m->mem);
}
//为内存块分配内存
void * mp_allock(mempool_t*m){
if(m==NULL||m->free_count==0) return NULL;
void*ptr =m->free_ptr;//ptr指向一块空闲的内存
m->free_ptr=*(char**)ptr;//m->free_ptr指向下一块空闲的内存 ☆
m->free_count--;
return ptr;
}
//为内存块释放内存
void mp_free(mempool_t*m,void*ptr){
*(char**)ptr=m->free_ptr;
m->free_ptr=(char*)ptr;
m->free_count++;
}
mempool_t M;
#if 0
int main(){
mempool_t m;
mp_init(&m,32);
void *p1 =mp_allock(&m);
printf("1:mp_alloc:%p\n",p1);
void *p2 =mp_allock(&m);
printf("2:mp_alloc:%p\n",p2);
void *p3 =mp_allock(&m);
printf("3:mp_alloc:%p\n",p3);
void *p4 =mp_allock(&m);
printf("4:mp_alloc:%p\n",p4);
mp_free(&m,p2);
void *p5 =mp_allock(&m);
printf("5:mp_alloc:%p\n",p5);
}
#endif
//对内存块的管理
//1.避免频繁分配
//2.长期运行出现的内存碎片
①内存池结构体
block_size
内存块的大小
free_count
空闲的内存块
free_ptr
指向空闲内存块的指针
mem
开辟内存页的大小
②内存池的初始化
内存块大小设置为size
开辟的内存页大小设置为MEM_PAGE_SIZE
空闲指针free_ptr 指向开头
空闲内存块的数量为m->free_count=MEM_PAGE_SIZE/size
通过for循环生成一串链表
③内存池的销毁
④为内存块分配内存
⑤为内存块释放内存