内存数据库
一般所谓内存数据库,是指能够与应用运行在同一个进程内的数据库,也就是说能够被嵌入到进程内运行的数据库。
比较著名的内存数据库软件,例如,Sqlite
;以及新生代的NOSql key-value
数据库bdb(Berkeley DB)
、lmdb(Lighning Memory-Mapped Database)
等。
数据库一般功能
- 对增删改查
CRUD
操作的支持 - 高效、丰富的索引,例如,对于
Blob
数据以及JSON
数据的支持 - 事务特性
- 持久化要求
我们的需求
- 能够直接操作结构体内存,避免数据库操作间的重复内存拷贝,要求内存数据库要面向结构体设计
- 能够完成增、删、改、查CRUD等数据库常规操作
- 提供高效、丰富的索引能力
- 因为是受限使用场景,可以接受其它方面数据库功能的弱化
深入分析需求
我们使用内存数据库的场景相对小众,因为能够直接操作结构体内存,虽然避免传统的在内存数据库接口层的内存隔离拷贝,但,因为内存对使用者直接曝露,应用修改内存后,特别是涉及到索引字段的修改时,必须特殊调用UpdateIndex(...)
接口,以保证数据库内信息的一致性。
但,特别地,如果不涉及索引字段修改,只涉及非索引相关字段,在查询获得结构体内存地址后,可以直接按照结构体的定义访问结构体的内存,而无需特殊数据库接口API
动作。
这种特殊需求的内存数据库,可以称之为面向结构体
的内存数据库需求。
因其简单、直接、高效之处,应该有其合适使用的舞台。
分析
我们知道传统的关系内存数据库,是能够与C
语言的结构体,以及面向对象语言中的对象,做很好模型到数据库的映射。
但,对于能够按照结构体操作内存的需求,因为传统的Sqlite
内存数据库,在接口处会进行拷贝隔离,会多引发一些拷贝。
而那些面向key-value
的键值数据库,更无法简单地支持面向结构体的内存数据库操作需求。
看来只能另辟蹊径 😃
如何设计呢
玩具级别
数据结构 | 如何使用 |
---|---|
Queue | 做忙闲资源管理 |
MAP | 做索引管理 |
Array | 做结构体存储管理 |
typedef struct
{unsigned int rowid;/*柔性数组*/unsigned char anyStruct[0];
} TableRowtypedef struct
{int i;int j;
} A;typedef struct
{TableRow row;A a;
} MemoryTableA;// 或动态申请内存
MemoryTableA atTableA[MAX_ROW];
- 我们知道
C
语言对于MAP
数据结构的支持能力很弱,而且单一使用MAP
做索引管理,适应能力也不够全面,所以,可称之为玩具版面向结构体的内存数据库。
丰富高效的索引能力支持,还离不开
B+
树、红黑树等数据结构的支持
架构师水平的设计
组件 | 如何使用 |
---|---|
Queue | 做忙闲资源管理 |
Sqlit | 负责结构体成员索引管理,会根据建内存表时的索引配置,创建位于Sqlite 的关系表,用做索引表 |
- | 索引表的列为结构体的特殊索引成员字段,并同时提供rowid 列,或结构体内存指针 列作为映射,见下表示意 |
Array | 做结构体内存存储管理 |
借助
Sqlite
丰富的索引支持能力,将面向结构体的内存数据库,特别是索引部分做的更通用和高效
Sqlite内索引表
field1 of struct | field2 of struct | … | array rowid | struct pointer |
---|---|---|---|---|
… | … | … | … | … |
在
field*
列上按照要求做唯一索引、非唯一索引、联合索引等
更高水平的要求
去除对于Sqlite
组件的依赖,相当于重复制造必要的轮子,但也可以使这种面向结构体的内存数据库开宗立派了!