Database 闪存数据库
LibXR 提供了两种轻量级的嵌入式键值数据库实现:DatabaseRawSequential 和 DatabaseRaw<MinWriteSize>。
它们均继承自抽象接口类 Database,用于嵌入式 Flash 等顺序写入存储介质,具备主备冗余、断电保护、类型安全封装,适配不同的存储对齐约束。
主要功能
- 主块和备份块冗余存储,每个块带版本头和写完标记,启动时据此判断主块是否完整,损坏时从备份块恢复;
- 提供统一接口
Database与模板封装Database::Key<T>,支持类型安全读写; - 两种实现模式:
DatabaseRawSequential:顺序写入,适用于不支持逆序写入的 Flash;DatabaseRaw<MinWriteSize>:面向最小写入单元受限的 Flash 后端,通过模板参数约束最小写入单元大小,并在构造时接收底层Flash与回收阈值;
- 键值更新后会由数据库实现自动保存;
Restore()用于清空数据库并回到初始状态。 DatabaseRaw的新增、更新和回收在任何时刻掉电,重新上电后每个键都是这次写入之前或之后的值;启动时完成或撤销被打断的写入,必要时整理一次存储区。
使用示例
创建数据库对象
示例在 Linux 上用 LinuxBinaryFileFlash<容量> 以文件模拟 Flash(#include "linux_flash.hpp",位于 driver/linux/),三个参数依次为文件路径、最小擦除单元和最小写入单元。
LinuxBinaryFileFlash<2048> flash("/tmp/flash.bin", 512, 8);
DatabaseRawSequential db(flash);
或使用 DatabaseRaw<MinWriteSize>:
LinuxBinaryFileFlash<2048> flash2("/tmp/flash2.bin", 512, 16);
DatabaseRaw<16> db(flash2, 128);
DatabaseRawSequential(flash, max_buffer_size = 256) 在 RAM 中分配 max_buffer_size 字节的缓冲区,取值不超过 Flash 容量的一半;DatabaseRaw<MinWriteSize>(flash, recycle_threshold = 128) 的模板参数不小于 Flash 的最小写入单元,启动或查找键时失效键数超过 recycle_threshold 会触发回收。
定义类型安全的键
int value = 42;
Database::Key<int> key(db, "my_key", value);
// 写入新值
key = 123;
// 读取回变量
key.Load();
printf("value = %d\n", static_cast<int>(key));
类型安全封装:Key
模板类 Database::Key<T> 提供类型安全的键值封装,构造时会尝试从数据库中加载键值,若不存在则添加新键。
struct Config { uint32_t baudrate; uint8_t mode; };
Database::Key<Config> cfg(db, "uart_cfg", {9600, 1});
// 写入配置
cfg = {115200, 0};
// 加载配置
cfg.Load();
T应为可平凡复制(trivially copyable)的类型,例如基本类型、数组和由它们组成的结构体;- 赋值操作
key = value会自动更新数据库; - 可通过
key.Load()显式从数据库刷新内容。 - 键的数据长度在第一次写入时固定。之后用不同长度的类型绑定同名键时,
Load()和Set()返回ErrorCode::FAILED,变量取构造时给出的初值,数据库中的原值保留。
常用接口
| 方法/操作 | 功能描述 |
|---|---|
Key<T>(db, name, init) | 绑定键;键不存在时以 init 写入数据库 |
Key<T>(db, name) | 同上,初值为全零 |
Key<T>::Set(T) / operator=(T) | 设置键值并写入数据库,返回 ErrorCode |
Key<T>::Save() | 把当前变量值写入数据库 |
Key<T>::Load() | 从数据库加载键值到变量 |
Key<T>::operator T() | 返回当前变量值(不从数据库加载) |
DatabaseRawSequential::Restore() | 清空顺序写数据库并重新初始化 |
DatabaseRaw<MinWriteSize>::Restore() | 清空 raw 数据库并重新初始化 |
DatabaseRaw<MinWriteSize>::Recycle() | 整理存储区,回收失效键占用的空间 |
工作机制概述
尽管不同实现的底层机制有所区别,但统一遵循如下设计原则:
- 使用主块和备块交替写入,确保写入过程可恢复;
- 键值以原始名值对顺序存放,块尾的固定标记表示该块已完整写入;
Init()内部自动判断有效块并尝试恢复数据;Restore()可主动清空主备数据并初始化空数据库;- 每个派生类自动处理页对齐、可用空间等底层逻辑,用户无需关心。
注意事项
- 请优先使用
Database::Key<T>类型封装进行读写; - 构造
Key时若键不存在,会把初值写入数据库;之后在key = val、key.Set(val)或key.Save()时写入,值与已存内容相同时不写 Flash; - 对所有键值数据类型要求为 POD 且可拷贝存储;
- 键不存在或数据长度与已存键不同时,
Set()返回ErrorCode::FAILED;DatabaseRaw回收后空间仍不足时返回ErrorCode::FULL;构造Key时添加新键失败,或底层 Flash 读、写、擦除失败,触发REQUIRE终止; - 若要显式清空数据库,可调用具体实现类的
Restore()。
应用场景
- 嵌入式设备参数存储;
- 状态断点续存;
- 多模块配置持久化;
- 内存受限设备上的键值存储;
示例测试
测试见 LibXR 仓库的 test/automatic/middleware/database/:database/test_database.cpp 检查 Key 的读写和错误码,raw_sequential/ 检查多键更新、重新打开和 Flash 故障处理,raw/ 另外覆盖长度不匹配和损坏恢复。