当前位置: 首页 > 操作系统&&数据库, 数据库 > 正文

C++ 自制Redis数据库(十三) 持久化AOF日志模块测试完毕

持久化AOF部分测试完毕

先说下我近况,我在追《女医》,看美剧,最近。今天下午做了下我数据库的AOF持久化模块,简单来说就是一个要求较高的日志模块。RDB和这个原理是一样的我思路上就这样了。

废话不多说,一般写日志,很多人就直接write 了,但是效率很低,这里就不详细解释了。

我AOF模块的设计

我是在为我的数据库写这个日志模块所以我需要考虑如下几个指标:

1.并发量不敢说很巨大。但是多个线程同时需要写日志这种可能性还是有的,一个连接给一个线程池中的线程干活,当业务很多的时候,好几个线程同时需要写日志是很可能的,所以需要来考虑。

2.吞吐量这个指标对于一个日志自系统来说都是很重要的,我们当然需要尽量利用硬盘的读写速度。

设计模式

我也查了很多的多路缓冲写日志的相关信息,也问了学长@王伟豪@苗帅,然后还是不晓得怎么搞,大概理解就是开辟缓冲区,然后加锁,当缓冲区到达一定程度的时候执行写文件操作。这样,策略很多,我也是眼花缭乱,我突然想起来之前看过Glibc 的malloc的设计,于是结合着我的需求,我这个多路缓冲日志就应运而生啦。

思路:

开辟几个缓冲区,每个缓冲区均有自己的锁,然后需要写日志的时候,就获取其中的一个锁,写入缓冲区,如果一个线程发现自己写完日志后,正好这个缓冲区满了,那么它就要负责去抢文件的锁,将整个缓冲区一股脑全写进文件里去。

AOF

 

就是这样,我希望每次来的日志都写进缓冲区去,然后当一个缓冲区满了就去抢写的锁。我们需要尽量将锁的争抢过程放到缓冲区上,文件写锁尽量少发生抢锁的情况,所以我们的缓冲区可以开得大一些。拖延时间,减少文件磁盘IO操作。

下面是一些代码和测试结果

#define AOF_LOG_MAXSIZE       256                            /*一条日志规定大小256字节*/
#define AOF_LOG_ELEM            (1<<17)                      /* 一个缓冲区大小,65536 条日志,每16MB写入文件一次*/
#define AOF_LOG_AREA             5                                 /* 设置5个缓冲区*/


&nbsp;

class AOF_FILE {                         /*底层的文件持久化*/
private:
int fd;
public:
std::mutex mutex_write;
AOF_FILE();
};
class AOF_LOG{
private:
char (*log_data)[AOF_LOG_MAXSIZE];                  /*一个日志缓冲区实例*/
AOF_FILE FILE_WRITE;
int useful;

public:
std::mutex mutex_log;                                           /*用来锁一个缓冲区*/
AOF_LOG();
~AOF_LOG();
bool AOF_LOG_ADD(const char *mesg,int flag);
bool WRITE();

};
class AOF_LOG_DO{
private:
AOF_LOG aof_buffer[AOF_LOG_AREA];   /*多个缓冲区,用了数组,没用链表,考虑到链表寻址效率低*/
int useful;
int resume;
public:
AOF_LOG_DO();
bool AOF_LOG_add(const char *mesg);
bool AOF_LOG_enforce_write();                       /*强制写入函数,会有一个线程每3秒执行一次强制写一次文件*/
bool AOF_LOG_READ(char *get);
int & AOF_LOG_getresume();
};



 

这里介绍一些核心的函数




bool AOF_LOG_DO::AOF_LOG_add(const char *mesg){

int length = std::strlen(mesg);
if(length > AOF_LOG_MAXSIZE){
return false;
}
int get_mutex = 1;
while(get_mutex){
for(int id = 0;id < AOF_LOG_AREA; id++){
if(aof_buffer[id].mutex_log.try_lock()){                   /*尝试获取一个缓冲区的锁,如果失败就尝试获取下一个的锁*/
aof_buffer[id].AOF_LOG_ADD(mesg,length);
aof_buffer[id].mutex_log.unlock();
get_mutex = 0;
break;
}
}
}

}

bool AOF_LOG::AOF_LOG_ADD(const char *mesg,int flag){

int length = std::strlen(mesg);
if(length != flag){
return false;
}
bzero(log_data[useful],AOF_LOG_MAXSIZE);                                 /*获取了一个锁进入一个缓冲区,将日志信息拷贝入我们的缓冲区*/
memcpy(log_data[useful],mesg,AOF_LOG_MAXSIZE);
useful++;
if(useful == AOF_LOG_ELEM){                                                          /*当一个缓冲区满了之后,写入文件*/
WRITE();
}
return true;
}

&nbsp;

&nbsp;

bool AOF_LOG::WRITE(){

while(1){
if(FILE_WRITE.mutex_write.try_lock()){                     /*这里获取文件的锁*/
break;
}
}
FILE *fp;
fp = fopen("LOG_AOF.log","ab+");
if(!fp){
return false;
}else{
for(int i = useful;i > 0;i--){
fwrite(log_data[i],AOF_LOG_MAXSIZE,1,fp);           /*将一个缓冲区所有的日志都写进文件*/
}
useful = 0;
fclose(fp);
FILE_WRITE.mutex_write.unlock();                        /*释放文件锁*/
return true;
}
}

 

测试结果

老实说,我也不知道什么样的效果才能算是称职的日志子系统,大家都看看,提提意见,这肯定不是最优的方法。

开4个线程,每个线程写500000(五十万)条日志,总共两百万条日志。

共花费1.9秒,平均每秒100万条日志,100万条日志 大约等于244MB/s.(基本把硬盘都用上了)

以上。

本文固定链接: http://zmrlinux.com/2016/02/24/c-%e8%87%aa%e5%88%b6redis%e6%95%b0%e6%8d%ae%e5%ba%93%ef%bc%88%e5%8d%81%e4%b8%89-%e6%8c%81%e4%b9%85%e5%8c%96aof%e6%97%a5%e5%bf%97%e6%a8%a1%e5%9d%97%e6%b5%8b%e8%af%95%e5%ae%8c%e6%af%95/ | Kernel & Me

该日志由 root 于2016年02月24日发表在 操作系统&&数据库, 数据库 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: C++ 自制Redis数据库(十三) 持久化AOF日志模块测试完毕 | Kernel & Me