实现一个简单SQL引擎
本文旨在帮助开发者在C/C++项目中,使用C++编程接口打造自己的SQL引擎。考虑到存储系统并非本文关注的重点,我们将简化存储层——使用内存表作为底层存储,这可以让我们更好关注引擎实现以及存储系统适配这些细节。
在深入详述实现细节以前,让我们简要概述实现一个简单内存表SQL引擎需要的步骤:
设计内存表结构
实现数据接口(
Catalog,TableHandler)子类:SimpleCatalog,SimpleTableHandler构造和执行引擎
完整的代码可参考simple_engine_demo
1. 内存表存储

上图描述了内存表的存储结构:
内存表每个索引维护一个
SegmentMemMapSegmentMemMap是key到时序表MemTimeTable的映射。key是根据数据的索引表达式值。key相同的数据按时间排序后组织成内存时序表MemTimeTable,最终绑定到key上。
2. 实现数据接口子类
以及TableHandler访问接口。
内部结构
我们在SimpleCatalog内部维护hybridse::type::Database database_和std::map<std::string, std::shared_ptr<SimpleCatalogTableHandler>>> table_handlers_来维护和管理数据库和表信息。其中, table_handler_是表名到SimpleCatalogTableHandler的映射。SimpleCatalogTableHandler的实现细节后面将会阐述。
接口实现
这里列出几处关键的函数和接口实现(更多细节可查阅simple_catalog.h和simple_catalog.cc)
构造函数
SimpleCatalog的构造函数几乎没有额外工作,进提供index-based-optimzation的开关初始化。这意味在,初始化后的SimpleCatalog的数据库元信息和数据都是空的。
数据库、表元信息查询接口
数据操作
对于Catalog来说,AddDatabase 和 InsertRows 都不是必须的。我们引入这两个操作方便添加元数据和数据。
内部结构
首先,它在内部维护table_storage来维护完整的[内存表存储](#1. 内存表存储):
其中,MemPartitionHandler就是MemSegmentMap的TableHandler实现,它内部就维护一个MemSegmentMap。使用MemPartitionHandler可以方便实现GetIterator,GetWindowIterator接口。
此外,它还维护了一个full_table_storage_,来维护无索引场景下的全表内存。这种设计对是对内存的浪费,但我们作为演示,性能不是我们考虑的重点。
接口实现
我们列出几处关键函数和接口实现(更新细节可查阅simple_catalog.h和simple_catalog.cc:
构造函数
SimpleCatalogTableHandler的构造函数通过分析传入的数据库名和表的TableDef来初始化表的元信息,包括表类型信息TableDef,索引列表IndexHint,列类型信息Types等。
数据库、表元信息查询接口
直接返回初始化好的各类表、索引、列信息即可
GetWindowIterator
使用MemPartitionHandler使得分组迭代器GetWindowIterator()的实现变得很简单,仅需要根据index_name找到对应的分组表MemPartitionHandler,然后直接调用MemPartitionHandler->GetWindowIterator()。更多细节请查询mem_catalog.cc
GetIterator
使用MemTableHandler使得全表迭代GetIterator()的实现变得很简单:
更多细节请查询MemTableHandler::GetIterator()
GetCount和At接口
对于没有准备好支持的接口,可以返回一些默认值,并打印ERROR日志。很遗憾,目前HybridSE还没有错误系统来管理这些错误。
3. 构造和执行引擎
构建引擎
构造
SimpleCatalog
准备数据库数据
配置引擎
EngineOption
我们简单只用默认引擎配置EngineOptions options;
构造
Engine示例
编译和运行
编译
执行
4. 运行SimpleEngineDemo
Last updated