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 程序烧录
- 参考 二、三 章节完成好硬件和软件环境的搭建。
- 此时按照如下步骤进行烧录程序。
- 按照如下步骤将 core 文件和 demo 例程烧录。
- 我们将波特率设置为 2000000,后续就可以看到如下实验效果
5.2.3 日志分析
普通日志分析
- 像如下这些日志信息,就是你存入的是什么数据,那么就会将他的数据类型和数据值进行打印。
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")))
- 在如下这段代码中,我们删除了 my_bool 这个键值,因此我们最终会打印出来一个空值。
-- 删除测试
fskv.del("my_bool")
local t = fskv.get("my_bool")
log.info("fskv", "my_bool", type(t), t)
boottime 键值分析
- 键值对的存储演示,每次芯片重启,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 键值分析
- 在下图中,将会有一行代码,在芯片的首次重启和后续重启过程中,存在日志打印的区别。
- 其原因是因如下图所示,如果不能理解,后面还有更详细的分析:
- 这里出现问题的原因是芯片首次启动,我们在 "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
- 后面我们调用如下代码,将 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}
- 当我们调用
fskv.set()
时候,mytable 键值中原来的值将会被覆盖,因此此时 mytable 中的值被覆盖为"age":20,"name":"wendal"
。
log.info("fskv", fskv.set("mytable", {age=20, name="wendal"}))
- 后续我们将 mytable 键值中的 name 设置为 nil 。这个相当于删除了 name 这个值。
log.info("fskv", fskv.sett("mytable", "name", nil))
- 因此最终我们看到的打印结果为
{"age":20}
。
log.info("fskv", fskv.get("mytable"), json.encode(fskv.get("mytable")))
- 现在我们再来看看第二次芯片重启。因为第一次芯片执行,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 键值分析
- 在 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 分区擦除
- 当我们重新烧录程序,希望将 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 库,可以实现对键值对进行排序的功能,从而支持有序集合的存储和检索。这有助于在需要按特定顺序处理数据时提高性能。
- 数据压缩与去重:为了节省存储空间和提高数据访问速度,可以引入数据压缩算法和去重机制。这些技术可以在数据写入时自动应用,并在读取时自动解压和恢复原始数据。