FlashDB嵌入式数据库之TSDB数据存储解析

一、驱动层:SFUD(Serial Flash Universal Driver) 是一款开源的串行 SPI Flash 通用驱动库
二、中间层:FAL(FLASH ABSTRACTION LAYER))FLASH 抽象层
三、应用层:FlashDB(FlashDB 是一款超轻量级的嵌入式数据库)
后记1:FlashDB嵌入式数据库之TSDB数据存储解析


FlashDB之TSDB解析

  • 一、初始化过程记录
  • 二、Flash中的存储格式
  • 三、实际使用率计算
  • 四、查询流程
  • 五、补充:性能测试


写在前面,好久才来写这边文章,读源码还是比较累的,先记录下来后期会议的时候方便一点。

一、初始化过程记录

先看一下帧头结构体

struct sector_hdr_data {
    uint8_t status[FDB_STORE_STATUS_TABLE_SIZE]; /**< sector store status @see fdb_sector_store_status_t */
    uint32_t magic;                              /**< magic word(`T`, `S`, `L`, `0`) */
    fdb_time_t start_time;                       /**< the first start node's timestamp */
    struct {
        fdb_time_t time;                         /**< the last end node's timestamp */
        uint32_t index;                          /**< the last end node's index */
        uint8_t status[TSL_STATUS_TABLE_SIZE];   /**< end node status, @see fdb_tsl_status_t */
    } end_info[2];
    uint32_t reserved;
};

根据计算sizeof(struct sector_hdr_data)= 40bytes
根据注释可以看出来,帧头中存储了扇区的状态、关键字、开始时间、结束时间、和预留了一个32位的数这样就可以实现对单个扇区的快速检索,
只需要读取头部的40个字节,就可以快速判断扇区是否已存满,扇区中的数据的开始时间和结束时间,如果需要检索数据本扇区中的数据是否符合条件,当然前提是时间戳是连续的,这个是默认条件。

二、Flash中的存储格式

源代码调用的层次很多,我们直接讲结果,有兴趣的自己看源码,都是开源的。
先看下实际的存储格式
可以看到帧头后面紧跟着的是数据头信息,每条数据头信息由16个字节组成

  • 数据头信息 = 数据状态 + 数据时间戳 + 数据长度 + 数据内容起始地址
  • 这样的好处是数据头信息是固定长度和固定位置的,可以方便我们快速根据时间戳检索,数据的内容是可以不定长的。
  • log内容用指针指向特定的地址,长度就不需要固定的
  • 每个扇区单独成块,就是一个最小的数据库单元

可以看到log1的数据头指向的数据内容在扇区的最后面,log2指向的内容再次后面,二边向中间挤压

三、实际使用率计算

每条记录固定占用空间为16字节

如果你的数据内容是16字节
实际使用率 = 16/(16+16) = 50%

一个扇区可以储存数量 = (4096 - 40) / 16 + 16 = 126(条)

1M FLASH有扇区数量 = 1M / 4096 = 256 (个)
1M FLASH可以存储数据 = 126*256 = 32256(条)
如果你的flash是8M的自己算一下。

四、查询流程

原生的api接口只有一个按时间查询的命令

/**
 * The TSDB iterator for each TSL by timestamp.
 *
 * @param db database object
 * @param from starting timestap
 * @param to ending timestap
 * @param cb callback
 * @param arg callback argument
 */
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)
{
    struct tsdb_sec_info sector;
    uint32_t sec_addr, oldest_addr = db->oldest_addr, traversed_len = 0;
    struct fdb_tsl tsl;
    bool found_start_tsl = false;

    if (!db_init_ok(db)) {
        FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db));
    }

//    FDB_INFO("from %s", ctime((const time_t * )&from));
//    FDB_INFO("to %s", ctime((const time_t * )&to));

    if (cb == NULL) {
        return;
    }

    sec_addr = oldest_addr;
    /* search all sectors */
    do {
        if (read_sector_info(db, sec_addr, §or, false) != FDB_NO_ERR) {
            continue;
        }
        /* sector has TSL */
        if ((sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL)) {
            if (sector.status == FDB_SECTOR_STORE_USING) {
                /* copy the current using sector status  */
                sector = db->cur_sec;
            }
            if ((!found_start_tsl && ((from >= sector.start_time && from <= sector.end_time)
                            || (sec_addr == oldest_addr && from <= sector.start_time))) || (found_start_tsl)) {
                uint32_t start = sector.addr + SECTOR_HDR_DATA_SIZE, end = sector.end_idx;

                found_start_tsl = true;
                /* search start TSL address, using binary search algorithm */
                while (start <= end) {
                    tsl.addr.index = start + ((end - start) / 2 + 1) / LOG_IDX_DATA_SIZE * LOG_IDX_DATA_SIZE;
                    read_tsl(db, &tsl);
                    if (tsl.time < from) {
                        start = tsl.addr.index + LOG_IDX_DATA_SIZE;
                    } else {
                        end = tsl.addr.index - LOG_IDX_DATA_SIZE;
                    }
                }
                tsl.addr.index = start;
                /* search all TSL */
                do {
                    read_tsl(db, &tsl);
                    if (tsl.time >= from && tsl.time <= to) 
                    {
                        /* iterator is interrupted when callback return true */
                        if (cb(&tsl, cb_arg)) {
                            return;
                        }
                    } else {
                        return;
                    }
                } while ((tsl.addr.index = get_next_tsl_addr(§or, &tsl)) != FAILED_ADDR);
            }
        } else if (sector.status == FDB_SECTOR_STORE_EMPTY) {
            return;
        }
        traversed_len += db_sec_size(db);
    } while ((sec_addr = get_next_sector_addr(db, §or, traversed_len)) != FAILED_ADDR);
}

就是从起始时间查询到结束时间,提供了一个回调接口,在这个接口中把数据显示在LCD屏上。

五、补充:性能测试

单纯的速度测试我没有做,原文给出了大概的速度,而且也没有实际的使用价值。
原文给的速度

msh />tsl bench
Append 13421 TSL in 5 seconds, average: 2684.20 tsl/S, 0.37 ms/per
Query total spent 1475 (ms) for 13422 TSL, min 0, max 1, average: 0.11 ms/per

我的环境是 STM32F4+TSDB+FATFS+U盘(USB_Host)
导出了10W条速度用时1分35秒(实际工程中测试)

导出至USB ≈ 1052per/s,差距还是挺大的。

 (1704条消息) FlashDB嵌入式数据库之TSDB数据存储解析_¥风笛¥的博客-CSDN博客

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值