跳转至

002:Air8101-LuatOS-软件指南-常用功能实现-键值对存储(fskv) 副本

一、fskv 概述

fskv 是 LuatOS 系统中的一个库,用于提供键值对(Key-Value)数据库功能。它允许开发者以键值对的形式存储和检索数据,这些数据会被持久化在 Flash 存储器上,确保在设备断电后数据不会丢失。fskv 库旨在替代旧的 fdb 库,并兼容 fdb 的函数,同时使用 fdb 的 Flash 空间。

  • 持久化存储:数据存储在 Flash 上,确保设备断电后数据不会丢失。
  • 功能丰富:提供了初始化、设置、获取、删除、清空等丰富的 API 接口。
  • 性能稳定:读写速度恒定,不受脏数据影响。
  • 数据长度限制: key 长度最大 63 字节,value 长度最大 4096 字节。

二、硬件环境

“古人云:‘工欲善其事,必先利其器。’在深入介绍本功能示例之前,我们首先需要确保以下硬件环境的准备工作已经完成。”

参考:硬件环境清单,准备以及组装好硬件环境。

三、软件环境

“凡事预则立,不预则废。”在详细阐述本功能示例之前,我们需先精心筹备好以下软件环境。

1. Luatools 工具

2. 内核固件文件(底层 core 固件文件):LuatOS-SoC_V10001_Air8101.soc;参考项目使用的内核固件

3. luatos 需要的脚本和资源文件

脚本和资源文件:https://gitee.com/openLuat/LuatOS-Air8101/tree/master/demo/wlan/softAP

lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;

准备好软件环境之后,接下来查看如何烧录项目文件到 Air8101 开发板,将本篇文章中演示使用的项目文件烧录到 Air8101 开发板中。

四、fskv 基本用法

4.1 本教程实现功能定义

fskv 提供了一种高效、灵活且持久的键值对存储解决方案。此次 demo 帮助开发者可以快速的熟悉运用 API 接口进行数据存储和管理。

4.2 文章内容应用

Air8101 开发板软硬件资料 :

API 参考 : fskv - kv 数据库

4.3 API 介绍

fskv.init()

初始化 fskv 数据库。当我们再调用 fskv 函数之前,需要调用该函数来初始化 fskv。

参数

返回值

返回值
类型
释义
取值
result
bool
返回初始化 fskv 数据库的结果
成功:true 失败:false

例子

if fskv.init() then  
    log.info("fskv", "数据库初始化成功")  
else  
    log.info("fskv", "数据库初始化失败")  
end

fskv.set(key, value)

设置键值对数据的函数,将指定的键(key)与对应的值(value)存储在键值数据库中,以实现数据的持久化存储。需要注意,如果键已存在,新的值将替代旧的值;如果键不存在,则会创建新的键值对

参数

参数
类型
释义
取值
key
string
键的名称
不能为空字符串,长度最大 63 字节
value
string/number/boolean/table
任意类型,表示要存储的值
不能为空值,最大为数据长度最大为 4095 字节;不可以是function, userdata, task

返回值

返回值
类型
释义
取值
result
bool
返回设置键值对成功与否的结果
成功:true 失败:false

例子

-- 设置成功打印 true ,失败打印 false
log.info("fskv", fskv.set("wendal", "goodgoodstudy"))
fskv.set("boottime", 666)
--        fskv     键值名            键值存储的类型                  键值存储的值
log.info("fskv", "boottime",     type(fskv.get("boottime")),     fskv.get("boottime"))

fskv.sett(key, skey, value)

在已存在的表中设置或更新子键skey)的值,实现对嵌套数据结构的管理。这个函数没有 fskv.set() 函数那么霸道,会直接覆盖整个 key 原来的值。fskv.sett() 只有当 key 和 skey 都相同时,才会对相应的 skey 进行更新。

注意:当 key 原来不是一个 table 那么也会进行覆盖。只有原来是 table 类型数据,才是追加。

-- 注意: 如果key不存在, 或者原本的值不是table类型,将会完全覆盖
-- 例如下列写法,最终获取到的是table,而非第一行的字符串
log.info("fskv", fskv.set("mykv", "123")) -- 键值此时存入值为 string 类型数据 "123"
log.info("fskv", "mykv",       type(fskv.get("mykv")),       fskv.get("mykv"))
log.info("fskv", fskv.sett("mykv", "age", "123")) -- 因为 mykv 不是一个 table,所以 mykv 会被覆盖上一行代码,再将其值保存为 {"age":"123"}
log.info("fskv", fskv.get("mykv"), json.encode(fskv.get("mykv")))

参数

参数
类型
释义
取值
key
string
主键的名称
不能为空字符串,长度最大 63 字节
skey
string
子键的名称
不能为空字符串,长度最大 63 字节
value
string/number/boolean/table
任意类型,表示要存储的值
空值表示删除;非空值最大为数据长度最大为 4095 字节;不可以是function, userdata, task

返回值

返回值
类型
释义
取值
result
bool
返回设置或更新子键值成功与否的结果
成功:true 失败:false/nil

例子

-- 本API在2023.7.26新增,注意与set函数区别
-- 设置数据, 字符串,数值,table,布尔值,均可
-- 但不可以是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", fskv.get("mytable"), json.encode(fskv.get("mytable")))
-- 注意: 如果key不存在, 或者原本的值不是table类型,将会完全覆盖
-- 例如下列写法,最终获取到的是table,而非第一行的字符串
log.info("fskv", fskv.set("mykv", "123"))
log.info("fskv", fskv.sett("mykv", "age", "123")) -- 保存的将是 {age:"123"}


-- 如果设置的数据填nil, 代表删除对应的key
log.info("fskv", fskv.sett("mykv", "name", "wendal"))
log.info("fskv", fskv.sett("mykv", "name")) -- 相当于删除
log.info("fskv", fskv.sett("mykv", "name", nil)) -- 相当于删除

fskv.get(key, skey)

根据键获取对应的数据。

参数

参数
类型
释义
取值
key
string
主键的名称
不能为空字符串,长度最大 63 字节
skey(可选)
string
子键的名称
可选,长度最大 63 字节

返回值

返回值
类型
释义
取值
result
string/number/boolean/table
对应键值中存储的数据
如果 key 或 skey 不存在,返回 nil;如果 key 对应的值存在,函数返回该值;如果 key 和 skey 都存在,返回skey 值

例子

-- 初始化 fskv 数据库
if fskv.init() then
    print("fskv 数据库初始化成功")

    -- 设置一些数据
    fskv.set("username", "luatos_user")
    fskv.sett("user_data", "user_id", 1001)
    fskv.sett("user_data", "is_active", true)

    -- 获取字符串类型的值
    local username = fskv.get("username")
    print("获取用户名:", username)

    -- 获取表中的子键值对
    local user_id = fskv.get("user_data", "user_id")
    print("获取用户ID:", user_id)

    local is_active = fskv.get("user_data", "is_active")
    print("获取活跃状态:", is_active)

else
    print("fskv 数据库初始化失败")
end

fskv.del(key)

永久删除键值数据库中指定键及对应的值

参数

参数
类型
释义
取值
key
string
键的名字
不能为空字符串,长度最大 63 字节

返回值

返回值
类型
释义
取值
result
bool
返回删除键数据的结果
成功:true 删除失败/键不存在:false

例子

-- 初始化 fskv 数据库
if fskv.init() then
    print("fskv 数据库初始化成功")

    -- 设置一些数据
    fskv.set("username", "luatos_user")
    fskv.set("user_id", 1001)

    -- 删除键 "username"
    local result = fskv.del("username")
    if result then
        print("成功删除 'username' 键")
    else
        print("删除 'username' 键失败")
    end

    -- 尝试获取已删除的键 "username"
    local username = fskv.get("username")
    print("获取 'username' 键的值:", username)  -- 应该输出 nil,表示已删除

else
    print("fskv 数据库初始化失败")
end

fskv.clear()

清空整个 kv 数据库。调用此函数会删除数据库中所有存储的键值对,彻底清空所有数据。

参数

返回值

返回值
类型
释义
取值
result
bool
返回清空整个 kv 数据库的结果
成功:true 失败:false

例子

-- 初始化 fskv 数据库
if fskv.init() then
    print("fskv 数据库初始化成功")

    -- 设置一些数据
    fskv.set("username", "luatos_user")
    fskv.set("user_id", 1001)

    -- 查看数据
    local username = fskv.get("username")
    local user_id = fskv.get("user_id")
    print("获取 'username' 键的值:", username)
    print("获取 'user_id' 键的值:", user_id)

    -- 清空整个数据库
    fskv.clear()
    print("数据库已清空")

    -- 尝试获取已清空的数据
    username = fskv.get("username")
    user_id = fskv.get("user_id")
    print("获取 'username' 键的值:", username)  -- 应该输出 nil
    print("获取 'user_id' 键的值:", user_id)  -- 应该输出 nil

else
    print("fskv 数据库初始化失败")
end

fskv.iter()

获取 kv 数据库的迭代器指针。与 fskv.next() 配合使用可以获取整个 kv 数据库的所有键值对。

参数

返回值

返回值
类型
释义
取值
result
userdata
与 fskv.next() 配合使用可以获取整个 kv 数据库的所有键值对
成功:迭代器指针 失败:nil

例子

-- 清空
local iter = fskv.iter()
if iter then
    while 1 do
        local k = fskv.next(iter)
        if not k then
            break
        end
        log.info("fskv", k, "value", fskv.kv_get(k))
    end
end

fskv.next(iter)

获取迭代器的下一个键。需要与 fskv.iter() 配合使用。

参数

参数
类型
释义
取值
iter
userdata
迭代器指针
由 fskv.iter() 返回的迭代器

返回值

参数
类型
释义
取值
result
string
获取的 key 名
成功获取返回字符串 key 的键名,否则返回 nil

例子

-- 清空
local iter = fskv.iter()
if iter then
    while 1 do
        local k = fskv.next(iter)
        if not k then
            break
        end
        log.info("fskv", k, "value", fskv.kv_get(k))
    end
end

fskv.status()

获取 kv 数据库状态

参数

返回值

**参数**
**类型**
**释义**
**取值**
used
int
已使用的空间,单位字节
最小为 0 ,最大为 maxs
maxs
int
总可用空间, 单位字节
固定值,由硬件的存储容量决定,或者通过数据库配置指定
kv_count
int
总kv键值对数量, 单位个
最小为 0

例子

local used,maxs,kv_count = fdb.kv_stat()
log.info("fdb", "kv", used,maxs,kv_count)

五、fskv 整体演示

5.1 源码

源码 : https://gitee.com/openLuat/LuatOS-Air8101/blob/master/demo/fskv/main.lua

5.2 演示

5.2.1 代码实现

-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "fskvdemo"
VERSION = "1.0.0"

-- sys库是标配
_G.sys = require("sys")

sys.taskInit(function()
    sys.wait(1000) -- 免得日志刷没了, 生产环境不需要

    -- 检查一下当前固件是否支持fskv
    if not fskv then
        while true do
            log.info("fskv", "this demo need fskv")
            sys.wait(1000)
        end
    end

    -- 初始化kv数据库
    fskv.init()
    log.info("fskv", "init complete")
    -- 先放入一堆值
    local bootime = fskv.get("boottime") -- 获取键值 boottime
    if bootime == nil or type(bootime) ~= "number" then
        bootime = 0  -- 如果键值 boottime 为 nil 或不是数字,则将其设为 0
    else
        bootime = bootime + 1 -- 如果键值 boottime 是有效的数字,则将其值增加 1
    end
    fskv.set("boottime", bootime)                 -- 向键值为 boottime 的空间中存入数值,每次重启该值加 1
    fskv.set("my_bool", true)                     -- 向键值为 my_bool 的空间中存入布尔值 true
    fskv.set("my_int", 123)                       -- 向键值为 my_int 的空间中存入整型数 123
    fskv.set("my_number", 1.23)                   -- 向键值为 my_number 的空间中存入浮点数 1.23
    fskv.set("my_str", "luatos")                  -- 向键值为 my_str 的空间中存入字符串 "luatos"
    fskv.set("my_table", {name="wendal",age=18})  -- 向键值为 my_table 的空间中存入数组,数组中包含 name 和 age 两个键值对,值分别为 "wendal" 和 18
    fskv.set("my_str_int", "123")                 -- 向键值为 my_str_int 的空间中存入字符串 "123"
    fskv.set("1", "123")                          -- 单字节key
    --fskv.set("my_nil", nil) -- 会提示失败,不支持空值

    --        fskv     键值名            键值存储的类型                  键值存储的值
    log.info("fskv", "boottime",     type(fskv.get("boottime")),     fskv.get("boottime"))
    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")))
    log.info("fskv", "my_str_int",   type(fskv.get("my_str_int")),   fskv.get("my_str_int"))
    log.info("fskv", "1 byte key",   type(fskv.get("1")),            json.encode(fskv.get("1")))

    -- 删除测试
    fskv.del("my_bool")
    local t = fskv.get("my_bool")
    log.info("fskv", "my_bool",      type(t),    t)

    -- 查询kv数据库状态
    -- local used, total,kv_count = fskv.stat()
    -- log.info("fdb", "kv", used,total,kv_count)

    -- fskv.clr()
    -- local used, total,kv_count = fskv.stat()
    -- log.info("fdb", "kv", used,total,kv_count)

    -- 压力测试
    -- local start = mcu.ticks()
    -- local count = 1000
    -- for i=1,count do
    --     -- sys.wait(10)
    --     -- count = count - 1
    --     -- fskv.set("BENT1", "--" .. os.date() .. "--")
    --     -- fskv.set("BENT2", "--" .. os.date() .. "--")
    --     -- fskv.set("BENT3", "--" .. os.date() .. "--")
    --     -- fskv.set("BENT4", "--" .. os.date() .. "--")
    --     fskv.get("my_bool")
    -- end
    -- log.info("fskv", mcu.ticks() - start)

    if fskv.sett then -- 查看是否提供了 fskv 模块是否提供了 sett 方法,如果提供继续执行
        -- 设置数据, 字符串,数值,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", fskv.get("mytable"), json.encode(fskv.get("mytable")))
        -- 注意: 如果key不存在, 或者原本的值不是table类型,将会完全覆盖
        -- 例如下列写法,最终获取到的是table,而非第一行的字符串
        log.info("fskv", fskv.set("mykv", "123")) -- 键值此时存入值为 string 类型数据 "123"
        log.info("fskv", "mykv",       type(fskv.get("mykv")),       fskv.get("mykv"))
        log.info("fskv", fskv.sett("mykv", "age", "123")) -- 因为 mykv 不是一个 table,所以 mykv 会被覆盖上一行代码,再将其值保存为 {"age":"123"}
        log.info("fskv", fskv.get("mykv"), json.encode(fskv.get("mykv")))

        -- 删除测试
        log.info("fskv", fskv.set("mytable", {age=20, name="wendal"}))          -- 这个会把原来的 mytable 中值进行覆盖
        log.info("fskv", fskv.sett("mytable", "name", nil))                     -- 删除 name 参数
        log.info("fskv", fskv.get("mytable"), json.encode(fskv.get("mytable"))) -- 最终只会打印出 {"age"=20}
    end
end)

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!

5.2.2 程序烧录

  1. 参考 二、三 章节完成好硬件和软件环境的搭建。
  2. 此时按照如下步骤进行烧录程序。

  1. 按照如下步骤将 core 文件和 demo 例程烧录。

  1. 我们将波特率设置为 2000000,后续就可以看到如下实验效果

5.2.3 日志分析

普通日志分析
  1. 像如下这些日志信息,就是你存入的是什么数据,那么就会将他的数据类型和数据值进行打印。
fskv.set("my_bool", true)                     -- 向键值为 my_bool 的空间中存入布尔值 true
fskv.set("my_int", 123)                       -- 向键值为 my_int 的空间中存入整型数 123
fskv.set("my_number", 1.23)                   -- 向键值为 my_number 的空间中存入浮点数 1.23
fskv.set("my_str", "luatos")                  -- 向键值为 my_str 的空间中存入字符串 "luatos"
fskv.set("my_table", {name="wendal",age=18})  -- 向键值为 my_table 的空间中存入数组,数组中包含 name 和 age 两个键值对,值分别为 "wendal" 和 18
fskv.set("my_str_int", "123")                 -- 向键值为 my_str_int 的空间中存入字符串 "123"
fskv.set("1", "123")                          -- 单字节key

--        fskv     键值名            键值存储的类型                  键值存储的值
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")))
log.info("fskv", "my_str_int",   type(fskv.get("my_str_int")),   fskv.get("my_str_int"))
log.info("fskv", "1 byte key",   type(fskv.get("1")),            json.encode(fskv.get("1")))

  1. 在如下这段代码中,我们删除了 my_bool 这个键值,因此我们最终会打印出来一个空值。
-- 删除测试
    fskv.del("my_bool")
    local t = fskv.get("my_bool")
    log.info("fskv", "my_bool",      type(t),    t)

boottime 键值分析
  1. 键值对的存储演示,每次芯片重启,boottime 的值都都进行加一。
local bootime = fskv.get("boottime") -- 获取键值 boottime
    if bootime == nil or type(bootime) ~= "number" then
        bootime = 0  -- 如果键值 boottime 为 nil 或不是数字,则将其设为 0
    else
        bootime = bootime + 1 -- 如果键值 boottime 是有效的数字,则将其值增加 1
    end
    fskv.set("boottime", bootime)                 -- 向键值为 boottime 的空间中存入数值,每次重启该值加 1

mytable 键值分析
  1. 在下图中,将会有一行代码,在芯片的首次重启和后续重启过程中,存在日志打印的区别。

  1. 其原因是因如下图所示,如果不能理解,后面还有更详细的分析:

  1. 这里出现问题的原因是芯片首次启动,我们在 "mytable" 键值中利用 fskv.sett() 函数存入的一大堆的数据,最终返回存储进入的结果 true 表示存储成功,因此在打印信息中我们可以看到连续四个相同打印。
-- 向 mytable 存入数据,打印存储成功西悉尼
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}))
-- 打印信息:
-- I/user.fskv        true
-- I/user.fskv        true
-- I/user.fskv        true
-- I/user.fskv        true
  1. 后面我们调用如下代码,将 mytable 键值中的内容全部打印。
log.info("fskv", fskv.get("mytable"), json.encode(fskv.get("mytable")))
-- 打印
--  I/user.fskv        table: 2802FE8C        {"bigd":{"name":"wendal","age":123},"wendal":"goodgoodstudy","timer":1,"upgrade":true}
  1. 当我们调用 fskv.set() 时候,mytable 键值中原来的值将会被覆盖,因此此时 mytable 中的值被覆盖为 "age":20,"name":"wendal"
log.info("fskv", fskv.set("mytable", {age=20, name="wendal"}))
  1. 后续我们将 mytable 键值中的 name 设置为 nil 。这个相当于删除了 name 这个值。
log.info("fskv", fskv.sett("mytable", "name", nil))
  1. 因此最终我们看到的打印结果为 {"age":20}
log.info("fskv", fskv.get("mytable"), json.encode(fskv.get("mytable")))
  1. 现在我们再来看看第二次芯片重启。因为第一次芯片执行,mytable 键值中存在一个 {"age":20} ,因此第二次打印将会比第一次,多一个 {"age":20}
log.info("fskv", fskv.get("mytable"), json.encode(fskv.get("mytable")))
-- 芯片首次启动打印信息
I/user.fskv        table: 2802FE8C        {"bigd":{"name":"wendal","age":123},"wendal":"goodgoodstudy","timer":1,"upgrade":true}
-- 芯片非首次启动打印信息
 I/user.fskv        table: 2802FC74        {"bigd":{"name":"wendal","age":123},"wendal":"goodgoodstudy","age":20,"timer":1,"upgrade":true}
mykv 键值分析
  1. 在 fskv.sett 方法使用时需要注意一点,当 key 原来不是一个 table 时,将会产生数据覆盖。
-- 注意: 如果key不存在, 或者原本的值不是table类型,将会完全覆盖
-- 例如下列写法,最终获取到的是table,而非第一行的字符串
log.info("fskv", fskv.set("mykv", "123")) -- 键值此时存入值为 string 类型数据 "123"
log.info("fskv", "mykv",       type(fskv.get("mykv")),       fskv.get("mykv"))
log.info("fskv", fskv.sett("mykv", "age", "123")) -- 因为 mykv 不是一个 table,所以 mykv 会被覆盖上一行代码,再将其值保存为 {"age":"123"}
log.info("fskv", fskv.get("mykv"), json.encode(fskv.get("mykv")))

5.3 程序烧录同时把 kv 分区擦除

  1. 当我们重新烧录程序,希望将 fskv 分区的内容进行清空。那么烧录的时候需要选择如下选项。

六、总结

fskv 库提供了一套完整的键值对(Key-Value)存储接口,允许开发者以灵活且持久的方式存储和检索数据。这些接口函数包括初始化数据库(fskv.init())、设置键值对(fskv.set()fskv.sett())、获取键值对(fskv.get())、删除键值对(fskv.del())、清空数据库(fskv.clear())、遍历数据库(fskv.iter()fskv.next())以及查询数据库状态(fskv.status())。

这些函数共同构成了一个功能强大的键值对存储系统,支持多种数据类型,包括字符串、数值、布尔值和 table 等。通过使用这些函数,开发者可以方便地管理存储在 Flash 存储器上的数据,实现数据的持久化和高效访问。

七、常见问题

7.1 数据写入失败

可能是由于 Flash 存储器写入次数超限、存储空间不足或数据库初始化失败等原因导致的。开发者应检查相关错误码或日志信息,以确定具体原因并采取相应的解决措施。

7.2 数据读取失败

可能是由于键不存在、数据库损坏或读取过程中发生异常等原因导致的。开发者应确保在读取数据前,数据库已正确初始化,并且键名正确无误。

7.3 性能问题

虽然 fskv 库提供了高效的键值对存储功能,但在处理大量数据时,仍可能出现性能瓶颈。开发者应根据实际应用场景,合理设计数据结构,并优化数据访问策略,以提高系统的整体性能。

7.4 数据一致性

在多线程或并发环境下,可能会出现数据一致性问题。开发者应确保在访问数据库时,采取适当的同步机制或锁策略,以保证数据的一致性和完整性。

7.5 数据覆盖

当使用 fskv.set() 方法时,将会产生数据覆盖问题。当使用 fskv.sett() 方法,key 值存在,但其存储的不是一个 table 也将存在数据覆盖问题。

7.6 fskv 库要去哪里下载

问题:想调用 KV 库进行掉电保存 执行 fskv.init() 报错 attempt to index global 'fskv' (a nil value)

答:这是因为当前的固件不支持 fskv 方法。我们可以去对应的模组 docs 文档中心查看。如果希望在代码中自动判断是否支持 fskv 可加入如下代码。如下代码含义为,不支持 fskv 方法,那么程序将会死循环打印 "this demo need fskv",支持 fskv 方法继续执行。

-- 检查一下当前固件是否支持fskv
if not fskv then
    while true do
        log.info("fskv", "this demo need fskv")
        sys.wait(1000)
    end
end

7.7 fskv 读写次数限制

读写次数有限制,最大可达到 80W 次以上。

八、扩展

  • 复合键与多级索引:在 fskv 库的基础上,可以引入复合键(由多个字段组成的键)和多级索引,以支持更复杂的数据查询和检索操作。这有助于提升数据访问的灵活性和效率。
  • 有序集合与排序:通过扩展 fskv 库,可以实现对键值对进行排序的功能,从而支持有序集合的存储和检索。这有助于在需要按特定顺序处理数据时提高性能。
  • 数据压缩与去重:为了节省存储空间和提高数据访问速度,可以引入数据压缩算法和去重机制。这些技术可以在数据写入时自动应用,并在读取时自动解压和恢复原始数据。