1. 设计原因
项目需要使用一个可以支持多线程的轻量级数据库,但类似于Mysql等完备的现成数据库过于大型,会造成功能冗余,于是决定基于std::unorder_map实现线程安全版哈希桶用于存放数据。
2. 采用unorder_map的原因
原先设计是用一个std::vetor存储数据,但vector查询效率低下,无法满足嵌入式需求,后面考虑使用map实现,可是map为红黑树,在性能上仍然不够理想,由此决定采用哈希表做为核心容器,可是自制哈希表需要手写哈希函数,较为复杂,最终决定采用std::unorder_map实现,该容器为标准库维护的哈希桶,平均查询性能为O(1),且对哈希冲突的处理较为完善。
3. 改造unorder_map的原因
unorder_map并未对多线程程序做线程安全处理,在多线程工程下容易出现bug,于是乎我决定采用mutex在需要修改数据时对unorder_map进行上锁。
4. 实现
#pragma once
/**
* @file database.hpp
* @author jiyilin 2474803745@qq.com
* @brief this is a database system
* @version 0.1
* @date 2023-07-23
*
* @copyright Copyright (c) 2023
*
*/
#include <unordered_map>
#include <mutex>
namespace DBMS
{
/**
* @brief this is a hashi bucket based database
*
* @tparam INDEX : hashi bucket key value
* @tparam DATA : data
* @memberof Data : hashi bucket for saving data
* @memberof m_read_write_lock : readers writer lock
*/
template <typename INDEX, typename DATA>
class DataBase
{
private:
std::unordered_map<INDEX, DATA> Data;
std::mutex m_read_write_lock;
public:
/**
* @brief Construct a new Data Base object
*
*/
DataBase() : m_read_write_lock(){};
/**
* @brief this function is used to add a key value pair to the database
*
* @param index : key
* @param value : data
* @return true : successfully added
* @return false : add failed
*/
bool Insert(INDEX index, DATA value)
{
std::unique_lock<std::mutex> lock(this->m_read_write_lock);
if (this->Data.count(index) != 0)
{
Log_WARM << index << "already exists , unable to add the data" << Log_END;
return false;
}
try
{
this->Data.insert(std::pair(index, value));
}
catch (const std::exception &e)
{
Log_ERROR << e.what() << Log_END;
return false;
}
return true;
}
/**
* @brief this function is used to search for data in the database
*
* @param index : key for the value to be searched for
* @param value : return search results
* @return true : the data to be searched exists
* @return false : the data to be searched does not exist
*/
bool Find(INDEX index, DATA &value)
{
std::unique_lock<std::mutex> lock(this->m_read_write_lock);
auto target = this->Data.find(index);
if (target == this->Data.end())
{
return false;
}
value = (*target).second;
return true;
}
/**
* @brief this function is used to delete a record in the database
*
* @param index : key to be deleted
* @return true : successfully deleted
* @return false : delete failed
*/
bool Delete(INDEX index)
{
std::unique_lock<std::mutex> lock(this->m_read_write_lock);
auto target = this->Data.find(index);
if (target == this->Data.end())
{
Log_WARM << index << "not present" << Log_END;
return false;
}
this->Data.erase((*target).first);
return true;
}
/**
* @brief this function is used to modify database data
*
* @param index : the key of the key value pair to be modified
* @param value : new data to be written
* @return true : successfully modified
* @return false : modification failed
*/
bool Change(INDEX index, DATA value)
{
std::unique_lock<std::mutex> lock(this->m_read_write_lock);
auto target = this->Data.find(index);
if (target == this->Data.end())
{
Log_WARM << index << "not present" << Log_END;
return false;
}
(*target).second = value;
return true;
}
/**
* @brief determine if the database is empty
*
* @return true : database has no data
* @return false : database has data
*/
bool is_empty()
{
std::unique_lock<std::mutex> lock(this->m_read_write_lock);
return this->Data.empty();
}
/**
* @brief get database size
*
* @return int
*/
int size()
{
std::unique_lock<std::mutex> lock(this->m_read_write_lock);
return this->Data.size();
}
/**
* @brief Destroy the Data Base object
*
*/
~DataBase(){};
};
} // namespace DBMS