写在开始
1、这是一篇简单的数据库实现,按照自己的思路来不要求效率高不要求实用性,为的是以博主这样的小白目光来琢磨数据库怎么去实现
2、博主是边写代码边写博客,从最开始的版本(可以说根本不能叫数据库的东西!)开始一步步实现到勉强能看的地步
3、基于key-value和c++,欢迎交流技术,如果喷请轻点
==================================================================================================================
其实我从大学时代就不喜欢数据库,感觉很麻烦所以不想学
最近NoSQL很火,而且感觉比传统的数据简单,尤其是key-value感觉像哈希表一样的操作非常舒服,所以尝试摸索下它的原理
首先,数据库有哪些操作呢?打开-关闭就不说了,关于数据的操作大概有:写入(包括覆写和插入)、查询、删除,其中最重要的就是查询(因为你删除和写入都要先找到原来的数据才能操作吧),所以我觉得查询的效率比较影响数据库性能。当然这些都是后话,先来一个不讲究效率的
先实现一个简单的,基于内存的数据库(比如redis)。
什么都按最简单的来不考虑性能
那么数据库该有的操作上面已经说了,先来建立一个类吧,不如叫smallsql
这个数据库只支持10240个数据,并且只支持string和int型数据
先来看看smallsql.h的定义
可能看到定义的朋友已经开始喷了,这是什么垃圾玩意,也能叫数据库,博主滚出CSDN之内的了
哈哈,不要着急嘛。前面说了这个文章是慢慢来逐步实现从最简单的开始到面前能看的样子
如果一来就是完整版,那不如找一个开源数据库大家一起分析代码来的爽快
这是打开和关闭数据库的函数
bool open(const std::string& sqlPath);
void close();
这是对数据库两种类型的读写(不同类型居然用两个不同的接口来操作,从来没看到什么数据库是这样的,我们下篇文章来解决)
std::string getStr(const std::string& key);
void setStr(const std::string& key, const std::string& value);
int getInt(const std::string& key);
void setInt(const std::string key, int value);
这是数据库的数据项(union被博主吃了吗?)
struct SqlData
{
std::string key;
std::string value_str;
int value_int;
};
好大概是这样了,来看看具体实现的源码
bool smallsql::open(const std::string& sqlPath)
{
FILE* fp = nullptr;
m_sqlPath = sqlPath;
fopen_s(&fp, sqlPath.c_str(), "r");
if (fp == nullptr)
{
return true;
}
int index = 0;
while (!feof(fp))
{
int key_len = 0;
fread_s(&key_len, 1, 1, 1, fp);
if (key_len == 0)
{
continue;
}
char* key = new char[key_len + 1];
fread_s(key, key_len, key_len, 1, fp);
key[key_len] = 0;
m_datas[index].key = std::string(key);
delete[] key;
int value_len = 0;
fread_s(&value_len, 1, 1, 1, fp);
if (value_len != 0)
{
char* value_str = new char[value_len + 1];
fread_s(value_str, value_len, value_len, 1, fp);
value_str[value_len] = 0;
m_datas[index].value_str = std::string(value_str);
delete[] value_str;
}
int value_int = 0;
fread_s(&value_int, 4, 4, 1, fp);
m_datas[index].value_int = value_int;
index++;
}
fclose(fp);
return true;
}
这个函数负责打开数据库,如果不是一个新的数据库(就是文件不存在)则负责读取数据到内存中,比较简单
close和这个函数基本一样,无非就是在关闭的时候得按照格式把数据库存到文件里面,就不贴代码了
接下来是getStr和setStr(int操作是一样的,就不单独说了)
std::string smallsql::getStr(const std::string& key)
{
for (int i = 0; i < smallsql::Max_Data_Count; ++i)
{
if (m_datas[i].key == key)
{
return m_datas[i].value_str;
}
}
return std::string();
}
void smallsql::setStr(const std::string& key, const std::string& value)
{
for (int i = 0; i < smallsql::Max_Data_Count; ++i)
{
if (m_datas[i].key == key)
{
m_datas[i].value_str = key;
return;
}
else if (m_datas[i].key.empty())
{
m_datas[i].key = key;
m_datas[i].value_str = value;
m_datas[i].value_int = 0;
return;
}
}
}
可以看出,函数使用了及其暴力的方法,遍历所有数据来实现数据的查找,而且存储的时候也会查找
单独说的一点,setStr中因为这个数据库没有删除操作(数据段都是连续的,不会出现中间有空),所有当读到第一个key是空的时候就可以存入了
接下来试试数据库的存储
可以看到,1W个数据的读写,分别都要花8秒左右(时间是以微秒字做单位),太慢了
做死一下,看看10W个数据呢,这里就不贴图了,负责的告诉你们也就80多秒而已,哈哈
下一篇文章中,要解决的就是数据操作的不友好,至少不能用多个API接口操作吧,而且存储方式也不行啊
写在后面
1、不用把这一篇文章实现的东西看做数据库,姑且叫做数据管理器?
2、文章的目的是告诉读者,数据库无非就是读取和保存数据的东西,如果不考虑效率大家也都能写出来嘛哈哈
3、抱着学习和娱乐的心态看这个系列吧