lashDB支持两种模式:FAL模式和文件模式。FAL模式用于嵌入式系统直接操作闪存,而文件模式可能在操作系统上使用文件模拟闪存。这种分层设计使得FlashDB可以灵活适应不同环境。
FlashDB 是一个为嵌入式系统设计的轻量级键值(KV)和时间序列(TS)数据库库,支持 Flash 存储。以下是实现方法详解:
1. 架构设计
FlashDB 采用模块化分层设计,核心分为 接口层 、存储引擎层 和 底层驱动层:
-
接口层 :提供 KVDB 和 TSDB 的 API,如
fdb_kv_set
、fdb_tsl_append
。 -
存储引擎层:实现数据存储逻辑,包括状态管理、垃圾回收(GC)、缓存机制。
-
底层驱动层:通过 FAL 或文件系统抽象 Flash 操作,实现跨平台兼容。
2. 关键数据结构
2.1 数据库基类 (struct fdb_db
)
-
作用:所有数据库类型(KVDB/TSDB)的基类。
-
核心成员:
struct fdb_db {
const char *name; // 数据库名称
fdb_db_type type; // 类型: FDB_DB_TYPE_KV 或 FDB_DB_TYPE_TS
union {
const char *dir; // 文件模式下的目录路径
fal_partition_t part; // FAL模式下的分区指针
} storage;
uint32_t sec_size; // 扇区大小(字节)
uint32_t max_size; // 数据库最大容量
bool init_ok; // 初始化标记
bool file_mode; // 是否为文件模式
// 锁函数指针,用户需实现
void (*lock)(fdb_db_t db);
void (*unlock)(fdb_db_t db);
};
2.2 键值数据库 (struct fdb_kvdb
)
-
继承自
fdb_db
,扩展 KV 相关功能:struct fdb_kvdb {
struct fdb_db parent; // 继承基类
struct fdb_default_kv default_kvs; // 默认键值对
bool gc_request; // GC 请求标志
struct fdb_kv cur_kv; // 当前操作的 KV
struct kvdb_sec_info cur_sector; // 当前扇区信息
// 缓存优化
struct kv_cache_node kv_cache_table[FDB_KV_CACHE_TABLE_SIZE];
struct kvdb_sec_info sector_cache_table[FDB_SECTOR_CACHE_TABLE_SIZE];
};
2.3 时间序列数据库 (struct fdb_tsdb
)
-
继承自
fdb_db
,扩展时间序列功能:struct fdb_tsdb {
struct fdb_db parent; // 继承基类
struct tsdb_sec_info cur_sec; // 当前扇区信息
fdb_time_t last_time; // 最后记录时间
fdb_get_time get_time; // 时间获取回调函数
size_t max_len; // 单条日志最大长度
bool rollover; // 是否启用滚动覆盖
};
3. 存储管理
3.1 扇区与状态管理
-
扇区状态 :每个扇区通过状态表(
status_table
)记录存储状态(如FDB_SECTOR_STORE_USING
)和脏标记(如FDB_SECTOR_DIRTY_TRUE
)。 -
状态操作:
// 设置状态位(如标记为已使用)
size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index);
// 获取当前状态
size_t _fdb_get_status(uint8_t status_table[], size_t status_num);
3.2 数据持久化
-
写入流程:
-
预写状态 :标记为
FDB_KV_PRE_WRITE
,防止写入中断导致数据损坏。 -
写入数据 :调用
_fdb_flash_write
写入键值对。 -
提交状态 :更新为
FDB_KV_WRITE
。
-
-
原子性保障:通过状态机确保操作原子性,崩溃恢复时根据状态回滚。
4. 缓存机制
4.1 KV 缓存 (kv_cache_table
)
-
LRU 策略 :通过
active
字段记录访问频度,淘汰最少使用的缓存项。 -
快速查找:根据键名 CRC 快速定位缓存,减少 Flash 扫描。
4.2 扇区缓存 (sector_cache_table
)
- 缓存热点扇区:存储频繁访问的扇区信息,加速扇区状态查询。
5. 垃圾回收 (GC)
-
触发条件:空间不足或手动调用 GC。
-
GC 流程:
-
标记无效数据:遍历扇区,标记已删除的 KV。
-
迁移有效数据:将有效数据复制到新扇区。
-
擦除旧扇区:释放空间,更新元数据。
-
6. 文件模式实现
6.1 文件操作抽象
-
POSIX/LIBC 适配:
#ifdef FDB_USING_FILE_POSIX_MODE
#define FDB_FILE_OPEN(path) open(path, O_RDWR | O_CREAT, 0666)
#else
#define FDB_FILE_OPEN(path) fopen(path, "rb+")
#endif
-
扇区文件管理 :每个扇区对应一个文件,文件名基于扇区地址生成(如
sec_0x0000.bin
)。
6.2 文件缓存优化
- 打开文件缓存 :通过
cur_file
数组缓存最近访问的文件句柄,减少频繁打开关闭。
7. 时间序列数据库特性
7.1 滚动覆盖 (Rollover)
-
启用时:当存储空间满时,覆盖最旧数据。
-
索引管理 :通过
end_idx
和start_time
快速定位数据范围。
7.2 时间范围查询
-
高效遍历:
void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg);
利用时间有序存储特性,二分查找快速定位时间窗口。
8. 错误处理与日志
-
错误码 :定义
fdb_err_t
枚举,如FDB_READ_ERR
、FDB_SAVED_FULL
。 -
日志分级:
#define FDB_DEBUG(...) // 调试信息(条件编译控制)
#define FDB_INFO(...) // 运行信息(始终输出)
9. 示例:KVDB 初始化与使用
#include <flashdb.h>
// 定义默认 KV
static struct fdb_default_kv_node default_kv_table[] = {
{"username", "armink", 0},
{"password", "123456", 0},
};
static struct fdb_default_kv default_kv = {
.kvs = default_kv_table,
.num = 2,
};
int main() {
fdb_kvdb_t kvdb = malloc(sizeof(struct fdb_kvdb));
// 初始化 KVDB(FAL 模式)
fdb_err_t err = fdb_kvdb_init(kvdb, "env", "kvdb_partition", &default_kv, NULL);
if (err != FDB_NO_ERR) {
FDB_INFO("KVDB init failed!\n");
return -1;
}
// 设置键值
fdb_kv_set(kvdb, "status", "ready");
// 读取键值
char *value = fdb_kv_get(kvdb, "status");
FDB_INFO("status: %s\n", value);
// 反初始化
fdb_kvdb_deinit(kvdb);
return 0;
}
10. 关键设计亮点
-
跨平台适配:通过 FAL 和文件模式支持裸机与 OS 环境。
-
数据完整性:CRC 校验 + 状态机确保崩溃恢复。
-
空间高效:GC 机制 + 对齐写入减少碎片。
-
性能优化:缓存 + 时间序列索引加速查询。
FlashDB 通过以上设计,在资源受限的嵌入式系统中实现了高效可靠的数据存储,适用于 IoT 设备、穿戴设备等场景。
FlashDB 作为专为嵌入式设计的轻量级数据库,其资源占用高度依赖配置和硬件环境。以下是关键资源消耗点及典型场景下的估算:
1. 内存占用 (RAM)
模块 | 典型配置 | RAM 占用估算 | 说明 |
---|---|---|---|
KV 缓存表 | FDB_KV_CACHE_TABLE_SIZE=64 |
512 字节 | 每个条目 kv_cache_node 占 8 字节(CRC16 + active + 地址) |
扇区缓存表 | FDB_SECTOR_CACHE_TABLE_SIZE=8 |
1 KB | 每个 kvdb_sec_info 结构约 128 字节 |
TSDB 元数据 | 单扇区存储 | 128 字节/扇区 | tsdb_sec_info 结构存储时间戳和索引信息 |
运行时栈 | 基本操作 | 1~2 KB | 函数调用栈,依赖递归深度和日志长度 |
总 RAM 消耗:
-
最小配置 (禁用缓存):约 1~2 KB
-
典型配置 (启用缓存):约 2~4 KB
-
高性能配置 (大缓存):可超过 10 KB
2. 存储占用 (Flash/EEPROM)
模块 | 典型场景 | Flash 占用估算 | 说明 |
---|---|---|---|
KVDB 元数据 | 每扇区头信息 | 32 字节/扇区 | 包含 magic、状态、剩余空间等 |
TSDB 元数据 | 每扇区头信息 | 48 字节/扇区 | 含时间戳、索引范围等 |
单条 KV 记录 | Key="temp", Value=25.5 | 50~100 字节 | 含头部(状态、CRC、长度) + 键值数据(对齐损耗) |
单条 TSL 记录 | 时间戳 + 温度数据 | 20~64 字节 | 含时间戳、状态、数据长度(依赖 max_len 配置) |
示例容量规划:
-
小型设备 (4 KB Flash):支持约 20~50 条 KV 或 100~200 条 TSL
-
中型设备 (64 KB Flash):可管理 500+ KV 或 5000+ TSL
-
损耗均衡 :建议预留 10~20% 空间以降低 GC 频率
3. CPU 开销
操作 | 时间消耗 | 说明 |
---|---|---|
KV 写入 | 5~50 ms | 依赖 Flash 擦除时间(如 STM32F1 SPI Flash 擦除 4KB 约 40ms) |
TSL 追加 | 1~10 ms | 无擦除操作时较快,仅写入数据 |
GC 过程 | 100 ms~2 s | 全扇区迁移时较长,建议在低负载时触发 |
KV 查询 | 0.1~5 ms | 缓存命中时极快,未命中需遍历 Flash |
4. 配置优化建议
降低资源占用的配置:
// fdb_cfg.h
#define FDB_KV_CACHE_TABLE_SIZE 0 // 禁用 KV 缓存
#define FDB_SECTOR_CACHE_TABLE_SIZE 0 // 禁用扇区缓存
#define FDB_WRITE_GRAN 8 // 适配 Flash 最小写入单位(如 NOR Flash)
#define FDB_USING_TIMESTAMP_32BIT // 使用 32 位时间戳(节省 4 字节/记录)
提升性能的配置:
// fdb_cfg.h
#define FDB_KV_CACHE_TABLE_SIZE 128 // 大缓存加速查询
#define FDB_SECTOR_CACHE_TABLE_SIZE 16 // 缓存更多扇区信息
#define FDB_USING_FAST_CRC32 // 启用硬件 CRC 加速校验
5. 对比其他嵌入式存储方案
方案 | RAM 占用 | Flash 占用 | 特性 |
---|---|---|---|
FlashDB | 2~10 KB | 低~中等 | 轻量、支持 KV+TS、崩溃安全 |
SPIFFS | 1~4 KB | 高(块管理开销大) | 类文件系统,适合大文件存储 |
EEPROM | 0 | 按需使用 | 无磨损均衡,适合低频小数据 |
SQLite | 50~200 KB | 高(代码体积大) | 完整 SQL 支持,适合复杂查询 |
6. 实际项目案例
智能家居温控器:
-
硬件:STM32F103 (64 KB Flash, 20 KB RAM)
-
需求:存储 10 个参数(KV) + 每 5 分钟记录温度(TSL,保存 7 天)
-
FlashDB 配置:
-
KVDB: 2 扇区(8 KB), 缓存 16 条目
-
TSDB: 24 扇区(48 KB), 支持滚动覆盖
-
-
资源占用:
-
RAM: 3.2 KB(缓存 + 运行时)
-
Flash: 56 KB(数据 + 元数据)
-
CPU: 每日 GC 约 200 ms
-
总结
-
最低配置 :可运行在 < 2 KB RAM + 4 KB Flash 的 Cortex-M0 设备
-
灵活扩展 :通过调整缓存和扇区数量适配从 8 位 MCU 到 Linux 嵌入式设备
-
适用场景:IoT 传感器、穿戴设备、工控参数存储等对资源敏感的实时系统