跳转至

fskv - kv数据库,掉电不丢数据

作者:马亚丹

一、概述

fskv 核心库是操作合宙 LuatOS 系统中的键值对(Key-Value)数据库的库,旨在替代旧的 fdb 库,并兼容 fdb 的函数,同时使用 fdb 的 Flash 空间。其作用是在 Flash 存储器中持久化存储键值对数据,允许开发者以键值对的形式存储和检索数据,并且这些数据会被持久化存储在 Flash 存储器上,确保设备断电后数据不会丢失。适用于物联网设备的配置信息、传感器数据等场景应用。

核心特点:

  • 持久化存储:数据写入 Flash,断电后不丢失;
  • 功能丰富:提供 fskv.init()(初始化)、fskv.set(key, value)(存储数据)、fskv.get(key)(读取数据)、fskv.del(key)(删除数据)等 API;
  • 稳定高效:读写速度恒定,不受 “脏数据” 影响,最高 10 万次均衡擦写;
  • 优化数据长度限制:如 value 最大 4096 字节,key 最大 63 字节。

合宙 fskv 核心库原理是在模组片上 flash 单独开辟了一个小区域,跑了个小文件系统,单独操作,实现类似于微型数据库的功能,只支持操作芯片自身的 flash 文件系统,不支持操作通过 lf 核心库或者 sfud 核心库挂载的文件系统。

简单说,fskv 核心库就是一个 “嵌入式设备里的小数据库”,专门用来安全、稳定地存放大块配置或业务数据,断电也不会丢。

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的 demo/fskv

local function fskv_test()
    -- 设置数据, 字符串,数值,table,布尔值,均可
    -- 但不可以是nil, function, userdata, task
    fskv.set("my_bool", true)
    fskv.set("my_int", 123)
    fskv.set("my_number", 1.23)
    fskv.set("my_str", "luatos")
    fskv.set("my_table", { name = "wendal", age = 18 })
    log.info("fskv", "获取my_bool", type(fskv.get("my_bool")), fskv.get("my_bool"))
    log.info("fskv", "获取my_int", type(fskv.get("my_int")), fskv.get("my_int"))
    log.info("fskv", "获取my_number", type(fskv.get("my_number")), fskv.get("my_number"))
    log.info("fskv", "获取my_str", type(fskv.get("my_str")), fskv.get("my_str"))
    log.info("fskv", "获取my_table", type(fskv.get("my_table")), json.encode(fskv.get("my_table")))

    if fskv.sett then
        -- 设置数据, 字符串,数值,table,布尔值,均可
        -- 但不可以是nil, function, userdata, task
        log.info("fskv", fskv.sett("mytable", "wendal", "goodgoodstudy"))
        log.info("fskv", fskv.sett("mytable", "upgrade", true))
        log.info("fskv", fskv.sett("mytable", "timer", 1))
        log.info("fskv", fskv.sett("mytable", "bigd", { name = "wendal", age = 123 }))

        -- 下列语句将打印出4个元素的table
        log.info("fskv", "获取mytable1", fskv.get("mytable"))
        log.info("fskv", "获取mytable1", fskv.get("mytable"), json.encode(fskv.get("mytable")))

        -- 删除测试
        log.info("fskv", fskv.sett("mytable", "nameage", { age = 18, name = "wendal" }))
        log.info("fskv", fskv.sett("mytable", "name"))
        log.info("fskv", "获取mytable2", fskv.get("mytable"), json.encode(fskv.get("mytable")))
    end
    --根据KV迭代器获取key值
    local iter = fskv.iter()
    log.info("kv数据库迭代器", iter)
    if iter then
        while 1 do
            local k = fskv.next(iter)
            log.info("kv迭代器获取下一个key", k)
            if not k then
                break
            end
            log.info("fskv", k, "value", fskv.get(k))
        end
    end
    --查询kv数据库状态
    local used, total, kv_count = fskv.status()
    log.info("获取kv数据库状态", "fskv", "kv", used, total, kv_count)
    log.info("清空整个kv数据库", fskv.clear())
    local used, total, kv_count = fskv.status()
    log.info("获取kv数据库状态", "fskv", "kv", used, total, kv_count)
end
sys.taskInit(fskv_test)

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用;

fskv 核心库库,没有常量。

四、函数详解

4.1 fskv.init()

功能

初始化 kv 数据库

注意事项

暂无

参数

返回值

local result= fskv.init()

有一个返回值 result

result

含义说明:初始化kv数据库
         成功时返回true,否则返回false     
数值类型:boolean
取值范围:true/false
注意事项:初始化失败时,需做好对应逻辑处理
返回示例:例如返回true时表示初始化kv数据库成功

示例

local result= fskv.init()
if result then
    log.info("fskv", "kv数据库初始化成功",result)
end

4.2 fskv.set(key, value)

功能

设置一对 kv 数据

注意事项

value 参数不能设为 nil、 function、 userdata、task 类型;

参数

key

参数含义:键的名称,即kv数据的key的名称
数据类型:string
取值范围:长度1~63字节
是否必选:必须传入此参数;
注意事项:不能是空字符串
参数示例:--如下所示,"wendal"即是设置kv数据时的key的名称
         log.info("fskv", fskv.set("wendal", "goodgoodstudy"))

value

参数含义:用户数据
数据类型:string/number/table/boolean
取值范围:长度1~4095字节
是否必选:必须传入此参数;
注意事项:参数不能设为nil function userdatatask
         可以是字符串/数值/table/布尔值, 数据长度最大4095字节
参数示例:--如下所示,"goodgoodstudy"即是设置kv数据时的用户数据
         log.info("fskv", fskv.set("wendal", "goodgoodstudy"))

返回值

local result= fskv.set(key, value)

有一个返回值 result

result

含义说明:设置一对kv数据
         成功时返回true,否则返回false     
数值类型:boolean
取值范围:true/false
注意事项:设置失败时,需做好对应逻辑处理
返回示例:例如返回true时表示设置一对kv数据成功

示例

-- 设置用户数据, 字符串、数值、table、布尔值均可
-- 用户数据不可以是nil, function, userdata, task

--如下所示设置用户数据是字符串
log.info("fskv", fskv.set("wendal", "goodgoodstudy"))
--如下所示设置用户数据是布尔值
log.info("fskv", fskv.set("upgrade", true))
--如下所示设置用户数据是数值
log.info("fskv", fskv.set("timer", 1))
--如下所示设置用户数据是table
log.info("fskv", fskv.set("bigd", {name="wendal",age=123}))

4.3 fskv.sett(key, skey, value)

功能

设置 table 内的键值对数据

注意事项

value 参数不能设为 nil、 function、 userdata、task 类型;

参数

key

参数含义:键的名称,即kv数据的key的名称
数据类型:string
取值范围:长度1~63字节
是否必选:必须传入此参数;
注意事项:不能是空字符串
参数示例:--如下所示,"mytable"即是设置table内的键值对数据时的key的名称
         log.info("fskv", fskv.sett("mytable", "wendal", "goodgoodstudy"))

skey

参数含义:table的key名称
数据类型:string
取值范围:长度1~63字节
是否必选:必须传入此参数;
注意事项:不能是空字符串
参数示例:--如下所示,"wendal"即是设置table内的键值对数据时的skey的名称
         log.info("fskv", fskv.sett("mytable", "wendal", "goodgoodstudy"))

value

参数含义:用户数据
数据类型:string/number/table/boolean
取值范围:nil,长度1~4095字节
是否必选:必须传入此参数;
注意事项:参数不能 function userdatatask
         可以是字符串/数值/table/布尔值, 数据长度最大4095字节,
         如果设为nil代表删除对应的key
参数示例:--如下所示,"goodgoodstudy"即是设置table内的键值对数据时的value
         log.info("fskv", fskv.sett("mytable", "wendal", "goodgoodstudy"))

返回值

local result= fskv.sett(key, skey, value)

有一个返回值 result

result

含义说明:设置table内的键值对数据
         成功时返回true,否则返回false/nil     
数值类型:boolean
取值范围:true/false/nil
注意事项:设置失败时,需做好对应逻辑处理
返回示例:例如返回true时表示设置table内的键值对数据成功

示例

-- 设置用户数据, 字符串、数值、table、布尔值均可
-- 用户数据不可以是function, userdata, task
-- 用户数据设为nil代表删除对应的key

--如下所示设置用户数据是字符串
log.info("fskv", fskv.sett("mytable", "wendal", "goodgoodstudy"))
--如下所示设置用户数据是布尔值
log.info("fskv", fskv.sett("mytable", "upgrade", true))
--如下所示设置用户数据是数值
log.info("fskv", fskv.sett("mytable", "timer", 1))
--如下所示设置用户数据是table
log.info("fskv", fskv.sett("mytable", "bigd", {name="wendal",age=123}))
-- 下列语句将打印出上面4个元素的table
--log打印  I/user.fskv mytable table: 0C7F5C30  table: 0C7F60F8 {"bigd":{"name":"wendal","age":123},"wendal":"goodgoodstudy","timer":1,"upgrade":true}
log.info("fskv", fskv.get("mytable"), json.encode(fskv.get("mytable")))

----
-- 注意: 如果key不存在, 或者key已存在但是原本的值不是table类型,再次设置同样key时原来的值将会完全覆盖
-- 例如下列两行的写法,最终获取到的是table 即{age:"123"},而非第一行的字符串123
log.info("fskv", fskv.set("mykv", "123"))
-- 保存的将是 {age:"123"}
log.info("fskv", fskv.sett("mykv", "age", "123")) 
----

----
-- 如果设置value用户数据为nil, 代表删除对应的key
--log打印  I/user.fskv true
--log打印  I/user.fskv删除
log.info("fskv", fskv.sett("mykv", "name", "wendal"))
log.info("fskv删除", fskv.sett("mykv", "name"))
----

4.4 fskv.get(key, skey)

功能

根据 key 获取对应的数据

注意事项

暂无

参数

key

参数含义:键的名称,即kv数据的key的名称
数据类型:string
取值范围:长度1~63字节
是否必选:必须传入此参数;
注意事项:不能是空字符串
参数示例:--如下所示,fskv.get("wendal")中的参数"wendal"即是获取数据时的key的名称
        --log打印  I/user.fskv goodgoodstudy
        log.info("fskv", fskv.set("wendal", "goodgoodstudy"))     
        log.info("fskv", fskv.get("wendal"))      

        -- 若需要"默认值", 对应非bool布尔值, 可以这样写
        local v = fskv.get("wendal") or "123"

skey

参数含义:次级key,仅当原始值为table时有效,相当于                  fskv.get(key)[skey]
数据类型:string
取值范围:长度1~63字节
是否必选:非必须传入此参数;
注意事项:暂无
参数示例:--如下所示,fskv.get("mytable", "wendal")中的参数"wendal"即是获取数据时的            key的名称,
        --log打印  I/user.fskv goodgoodstudy
        log.info("fskv", , fskv.sett("mytable", "wendal", "goodgoodstudy"))
        log.info("fskv",  fskv.get("mytable", "wendal"))

返回值

不带 skey 参数的写法

fskv.set("wendal","goodgoodstudy")

local v= fskv.get("wendal")

有一个返回值 v

v

含义说明:与key对应的数据
         数据存在则返回数据,否则返回nil   
数值类型:any
取值范围:无特别限制
注意事项:未获取失败数据时,需做好对应逻辑处理
返回示例:例如返回 goodgoodstudy时表示获取数据成功

带 skey 参数的写法

fskv.sett("mytable", "wendal", "goodgoodstudy")

local v= fskv.get("mytable","wendal")

有一个返回值 v

v

含义说明:与skey对应的数据
         数据存在则返回数据,否则返回nil   
数值类型:any
取值范围:无特别限制
注意事项:未获取失败数据时,需做好对应逻辑处理
返回示例:例如返回 goodgoodstudy时表示获取数据成功

示例

-- 例如下列写法,最终获取到的是数据123
log.info("fskv", fskv.set("mykv", "123"))
log.info("fskv", fskv.get("mykv"))


log.info("fskv", fskv.set("bigd", {name="wendal",age=123}))
-- 例如下列写法,最终获取到的是table: 0C7F6798  
log.info("fskv",  fskv.get("bigd"))
-- 例如下列写法,最终获取到的是wendal   
log.info("fskv",  fskv.get("bigd","name"))
-- 例如下列写法,最终获取到的是123
log.info("fskv", fskv.get("wendal") or "123")

4.5 fskv.del(key)

功能

根据 key 删除数据

注意事项

暂无

参数

key

参数含义:键的名称,即kv数据的key的名称
数据类型:string
取值范围:长度1~63字节
是否必选:必须传入此参数;
注意事项:不能是空字符串
参数示例:--如下所示,"wendal"即是删除kv数据时的key的名称
         log.info("fskv", fskv.del("wendal"))

返回值

local result= fskv.del("wendal")

有一个返回值 result

result

含义说明:删除一对kv数据
         成功时返回true,否则返回false     
数值类型:boolean
取值范围:true/false
注意事项:返回失败时,需做好对应逻辑处理
返回示例:例如返回true时表示删除一对kv数据成功

示例

log.info("fskv", fskv.set("wendal", "goodgoodstudy"))
--如下写法,删除上述设置的kv数据,返回true,表示删除成功
log.info("fskv", fskv.del("wendal"))

4.6 fskv.clear()

功能

清空整个 kv 数据库

注意事项

暂无

参数

返回值

local result= fskv.clear()

有一个返回值 result

result

含义说明:清空整个kv数据库
         成功时返回true,否则返回false     
数值类型:boolean
取值范围:true/false
注意事项:返回失败时,需做好对应逻辑处理
返回示例:例如返回true时表示初始化kv数据库成功

示例

local result= fskv.clear()
if result then
    log.info("fskv", "kv数据库清空成功",result)
end

4.7 fskv.iter()

功能

kv 数据库迭代器

注意事项

该接口作用是创建一个 “迭代器指针”(可以理解为 “遍历的起始游标”)。

再通过 fskv.next(iter) 配合这个指针,就能逐个获取 KV 数据库中的键(当返回 nil 时,表示遍历完所有键)。

这样你就可以通过 “迭代器 + next 方法” 的组合,遍历整个 KV 数据库的所有键值对(拿到键后,再通过 fskv.get 等方法取对应值),而不需要关心 KV 数据库内部是如何存储的。

参数

返回值

local iter = fskv.iter()

log.info("kv数据库迭代器", iter)

有一个返回值 iter

iter

含义说明:kv数据库迭代器
         成功返回迭代器指针,否则返回nil   
数值类型:userdata
取值范围:无特别限制
注意事项:返回失败时,需做好对应逻辑处理
返回示例:例如返回userdata: 0C7F5158时表示获取kv数据库迭代器成功

示例

local iter = fskv.iter()
log.info("kv数据库迭代器", iter)

4.8 fskv.next(iter)

功能

根据 kv 迭代器指针获取下一个 key

注意事项

暂无

参数

iter

参数含义:kv迭代器fskv.iter()返回的指针
数据类型:userdata
取值范围:无特别限制
是否必选:必须传入此参数;
注意事项:不能是空字符串
参数示例:--如下所示,"iter"即是获取下一个key时的kv迭代器指针
         local k = fskv.next(iter)

返回值

local k = fskv.next(iter)

有一个返回值 k

k

含义说明:根据kv迭代器指针获取的下一个key
         成功返回字符串key值, 否则返回nil  
数值类型:string
取值范围:1~63字节
注意事项:返回失败时,需做好对应逻辑处理
返回示例:例如按照4.2fskv.set(key, value)的示例设置了4kv数据,下方示例返回upgrade等key时表示获取到下一个key成功

示例

local iter = fskv.iter()
    log.info("kv数据库迭代器", iter)
    if iter then
        while 1 do
            local k = fskv.next(iter)
            log.info("kv迭代器获取下一个key", k)
            if not k then
                break
            end
         log.info("fskv", k, "value", fskv.get(k))
     end
end

4.9 fskv.status()

功能

获取 kv 数据库状态

注意事项

暂无

参数

返回值

local used, total,kv_count = fskv.status()

有三个返回值 used, total,kv_count

used

含义说明:已使用的空间,单位字节;
         成功时返回已使用空间大小    
数值类型:int
取值范围:0~65536
注意事项:返回失败时,需做好对应逻辑处理
返回示例:例如返回8192时表示已使用8192字节的空间

total

含义说明:总可用空间, 单位字节;
         成功时返回总可用空间大小    
数值类型:int
取值范围:65536
注意事项:支持LuatOS的合宙模组总可用fskv空间是64K
返回示例:例如返回65536时表示总可用空间是65536字节

kv_count

含义说明:总kv键值对数量, 单位个;
         成功时返回总kv键值对数量,    
数值类型:int
取值范围:0~
注意事项:暂无
返回示例:例如返回8时表示已存在8KV数据

示例

-- log打印  I/user.获取kv数据库状态 fskv kv 8192 65536 10
local used, total, kv_count = fskv.status()
log.info("获取kv数据库状态", "fskv", "kv", used, total, kv_count)

五、产品支持说明

支持 LuatOS 开发的所有产品都支持 fskv 核心库。