当前位置: 首页 > news >正文

开源边缘KV时序数据库 qv-lite

项目地址

https://github.com/mababaNiubi/qv-lite.git
https://gitee.com/QUIWEI/qv-lite.git

目录

  • 特性
  • 安装
  • 使用方法
    • 打开与关闭
    • 写入
    • 查询
    • 表管理
    • 数据类型
    • 聚合
    • 条件过滤
  • 配置项
  • 压缩算法
  • 数据编码
  • 依赖

特性

  • 嵌入轻量— 专为边缘网关、工业控制器、IoT 设备等 CPU/内存/磁盘受限场景设计。
  • 代码精简— 紧凑、可审计的核心代码,极少三方依赖。直观的目录布局, 不依赖重型数据库引擎,便于排查、备份和迁移。
  • 高性能写入— 单线程同步设计。单点写入性能800 万+ 点/秒
  • 自适应类型编码— 运行时自动识别输入数据的结构,根据数据类型选择合适的压缩编码器,达到最优存储效率。
  • 高压缩率— 紧凑的数据编码加二次块压缩,实现极致的存储效率。
  • 多表支持— K/V列式数据库,管理多个独立表,每个表可拥有独立的 Schema 定义和列集合。
  • 降采样查询— 长时间范围查询支持滑动窗口聚合(avg / min / max)。
  • 数据过期— 可配置的基于时间的数据自动清理。
  • 去重与最小间隔— 可配置的去重窗口和最小写入间隔,防止重复数据。
  • 块级索引— 基于二分搜索的块索引,快速过滤时间范围,无需扫描无关数据。

安装

go get github.com/mababaNiubi/qv-lite/tsdb

使用方法

打开与关闭

import("context""github.com/mababaNiubi/qv-lite/tsdb")db,err:=tsdb.Open(tsdb.Config{Path:"./qvLite-data",},context.Background())iferr!=nil{panic(err)}deferdb.Close()

Open在给定路径创建或打开数据库。default表在首次使用时自动创建。Close()会刷新所有缓冲数据并释放资源。

写入

import("time""github.com/mababaNiubi/variant")// 写入 default 表(tableName = "" 或 "default")written,err:=db.Write("","sensor_temp",time.Now().UnixNano(),variant.New(25.6))// 写入指定的表written,err:=db.Write("metrics","cpu_usage",time.Now().UnixNano(),variant.New(42.5))
  • tableName— 空字符串或"default"写入自动创建的默认表。
  • tag— 时序标识(如传感器名称、指标 key)。
  • timestamp— Unix 时间戳,单位为纳秒
  • value— 使用variant.New(v)包装任意支持的 Go 值。
  • 返回(true, nil)表示写入成功;(false, nil)规则跳过。

查询

// 范围查询 + 降采样 — 适用于长时间范围points,err:=db.Query("default","sensor_temp",time.Now().Add(-1*time.Hour).UnixNano(),// startTime (ns)time.Now().UnixNano(),// endTime (ns)1000,// maxNumber 返回最大点数tsdb.AvgFusion,// 聚合模式nil,// 条件过滤(nil = 无过滤))// 获取全部原始数据(不降采样)points,err:=db.QueryAll("default","sensor_temp",time.Now().Add(-30*time.Minute).UnixNano(),time.Now().UnixNano(),nil,)// 获取 tag 的最新值point,err:=db.QueryLatest("default","sensor_temp")ifpoint!=nil{fmt.Printf("latest: time=%d, value=%v\n",point.Tms,point.V)}
方法说明
Query(tableName, tag, startTime, endTime, maxNumber, polymerization, cond)范围查询。时间跨度 > 1 小时时返回最多maxNumber个降采样点;≤ 1 小时时直接返回全部原始数据。
QueryAll(tableName, tag, startTime, endTime, cond)返回范围内的全部原始数据点,不做数量限制。
QueryLatest(tableName, tag)返回指定 tag 的最新一条数据。

所有时间戳单位为纳秒(UnixNano)。maxNumber设为 0 时默认 10000。

表管理

// 创建简化指标表,拥有最高的写入性能err:=db.CreateTable(tsdb.TableInfo{ColumnAttribute:tsdb.ColumnAttribute{Name:"device",Desc:"attributes",Type:tsdb.ColumnTypeFloat,FloatPrecision:2,},})// 创建多列表err=db.CreateTable(tsdb.TableInfo{ColumnAttribute:tsdb.ColumnAttribute{Name:"metrics",Desc:"Dev01.CPU",Type:tsdb.ColumnTypeStructure,Structure:[]tsdb.ColumnAttribute{{Name:"value",Type:tsdb.ColumnTypeFloat,FloatPrecision:2},{Name:"quality",Type:tsdb.ColumnTypeInt},{Name:"status",Type:tsdb.ColumnTypeString},},},})// 创建自适应 Schema 的表(运行时自动发现字段,比固定结构慢)err=db.CreateTable(tsdb.TableInfo{ColumnAttribute:tsdb.ColumnAttribute{Name:"events",Desc:"动态事件日志",Type:tsdb.ColumnTypeUnknown,// 自适应 — 运行时发现字段格式},})

数据类型

常量类型说明
ColumnTypeUnknown(0)自适应运行时自动检测嵌套结构
ColumnTypeInt(1)整数有符号 64 位整数
ColumnTypeFloat(2)浮点数64 位浮点数,可配置小数精度
ColumnTypeString(3)字符串UTF-8 字符串
ColumnTypeBool(4)布尔值true / false
ColumnTypeJson(5)JSON任意嵌套 variant
ColumnTypeStructure(6)固定结构体预定义列 Schema

聚合

常量说明
AvgFusion0滑动窗口取平均值
MinFusion1滑动窗口取最小值
MaxFusion2滑动窗口取最大值

条件过滤

查询方法的cond参数支持按值过滤数据点:

// 等值过滤 — 仅返回 value == "ok" 的点cond:=tsdb.Condition{Operator:tsdb.OpEqual,Value:variant.New("ok"),}points,err:=db.QueryAll("default","status",startTs,endTs,cond)// 逻辑与 — 组合多个条件logicalCond:=tsdb.LogicalCondition{Op:tsdb.LogicalAnd,Cond:[]any{tsdb.Condition{Operator:tsdb.OpGreaterThan,Value:variant.New(80)},tsdb.Condition{Operator:tsdb.OpLessThan,Value:variant.New(100)},},}points,err:=db.QueryAll("default","cpu",startTs,endTs,logicalCond)

配置项

Config

字段类型默认值说明
Pathstring"./qvLite-data"数据库数据存储路径
WalConfigWalConfigWAL 配置(见下)
MaxSegmentSizeint6467108864(64MB)单个段文件最大大小(字节)
MaxSegmentTimeIntervalint640(不限制)单个段文件最大时间跨度(秒)
MaxStorageTimeint643600(1 小时)拒绝远大于当前时间的数据写入
ExpirationMinuteTimeint640(禁用)数据过期时间(分钟),写入时清理
DedupWindowMsint640(禁用)去重窗口(毫秒),两次写入窗口内相同的值跳过写入
MinIntervalMsint640(禁用)两次写入之间的最小间隔(毫秒),小于这个值跳过写入
SecondaryCompressionNamestring"zstd"块压缩算法:"zstd""lz4""snappy""gzip""none"

WalConfig

字段类型默认值说明
MaxCacheSizeint6467108864(64MB)WAL 内存缓存最大大小(字节)
MaxFileNumberint最大 WAL 文件数量
CloseBufferboolfalse是否关闭内存 WAL 缓冲
MaxBufferBatchSizeint10000排序刷盘前的最大缓冲条目数

CloseBuffer行为说明:

场景CloseBuffer = trueCloseBuffer = false
乱序写入同 key 下禁止乱序时间写入MaxBufferBatchSize批处理范围内允许乱序写入
写入/查询性能较低较高
异常中断数据安全可能丢失部分缓冲数据
内存占用通常在MaxCacheSize范围内MaxCacheSize的 3~4 倍

可通过限制MaxCacheSize大小来控制数据库内存使用。

压缩算法

数据类型策略
时间戳delta-of-delta + 缩放 + simple8b / RLE
整数delta + zigzag + simple8b / RLE
浮点数XOR-delta + 尾数截断(基于小数精度)
布尔值位打包(每字节 8 个)或全 True/False RLE
字符串uvarint 长度前缀拼接 + Snappy
JSONvariant 二进制序列化 + LZ4
固定结构体按列独立编码,每列使用对应类型的子编码器
自适应结构体自动发现 Map 中的列,递归嵌套,自描述格式

数据编码

磁盘布局

{db_path}/ table.json # 表元数据 default/ # 默认表目录 meta.json # 标签字典 data/ # 段数据文件 1234567890.tsb # 段文件(BlockFile 格式) 1234567890.tsb.idx # 段块索引 wal/ # WAL 目录 1234567890.wal # WAL 日志文件

段文件 (.tsb) — BlockFile

每个.tsb文件是一个BlockFile,包含若干压缩数据块,末尾附有块级索引:

┌──────────────────────────────────────────────────────┐ │ BlockFile (.tsb) │ ├───────────────┬──────────────────────────────────────┤ │ FileHeader │ Magic、版本、事务状态、压缩算法等 │ ├───────────────┼──────────────────────────────────────┤ │ 物理块 0 │ ┌ LenPrefix ───────────────────────┐ │ │ │ │ CompressedPayload │ │ │ │ │ = 多个逻辑块拼接后一起压缩 │ │ ├───────────────┤ └──────────────────────────────────┘ │ │ 物理块 1 │ ┌ LenPrefix ───────────────────────┐ │ │ │ │ CompressedPayload │ │ │ │ │ ... │ │ ├───────────────┤ └──────────────────────────────────┘ │ │ ... │ │ ├───────────────┼──────────────────────────────────────┤ │ 块索引 │ Count + IndexEntry × M │ │ │ · RawOff → 解压后逻辑偏移 │ │ │ · CompOff → 压缩后物理偏移 │ │ │ · CompLen → 压缩后大小 │ │ │ · Crc32 → 校验码 │ └───────────────┴──────────────────────────────────────┘

块索引位于文件末尾,记录每个物理块的物理位置与解压后逻辑偏移的映射关系,支持按偏移直接定位到任意块。

段索引文件 (.tsb.idx)

.tsb段文件配套存在,用于时间范围和标签维度的快速过滤:

┌──────────────────────────────────────────────────┐ │ 段索引 (.tsb.idx) │ ├──────────────┬───────────────────────────────────┤ │ Header │ Magic、BlockCount、MinTime、MaxTime │ ├──────────────┼───────────────────────────────────┤ │ Entry [0] │ TagCode、MinTime、MaxTime、 │ │ │ Offset、DataSize │ ├──────────────┼───────────────────────────────────┤ │ Entry [1] │ ... │ ├──────────────┼───────────────────────────────────┤ │ ... │ (每个逻辑块一条记录) │ └──────────────┴───────────────────────────────────┘

每条记录描述一个逻辑块属于哪个标签列、覆盖的时间范围、以及在.tsb文件中的位置。查询时通过二分搜索快速定位相关块并跳过无关数据。

逻辑块

列编码器写入的基本单元,多个逻辑块拼接后压缩为一个物理块:

┌──────────────────────────────────────────────────────────┐ │ 一个逻辑块 │ ├────────────────┬─────────────────────────────────────────┤ │ SegmentHeader │ Payload(压缩后的数据) │ │ │ ┌─────────────────┬──────────────────┐ │ │ · TagCode │ │ CompressedValue │ CompressedTime │ │ │ · MinTime │ │ (类型编码器) │ (delta-of-delta)│ │ │ · MaxTime │ └─────────────────┴──────────────────┘ │ │ · DataSize │ │ │ · CRC32 │ │ └────────────────┴──────────────────────────────────────────┘

负载格式

标量类型(Int、Float、String、Bool、Json):

┌────────────────────┬─────────────────────┬──────────────────┐ │ ValueByteLength │ CompressedValueData │ CompressedTime │ │ (长度前缀) │ (类型编码器压缩) │ (TimeEncoder) │ └────────────────────┴─────────────────────┴──────────────────┘

结构体类型(固定 / 自适应结构体):

┌────────┬──────────┬──────────┬─────┬──────────────────┬──────────────────┐ │ Marker │ Field0Len│ Field1Len│ ... │ FieldData Concat │ CompressedTime │ │ (1B) │ (各 8B) │ │ (各字段独立压缩) │ (TimeEncoder) │ └────────┴──────────┴──────────┴─────┴──────────────────┴──────────────────┘

结构体的每个字段使用各自类型对应的压缩器独立编码后拼接。Marker 标记区分固定结构体(预注册列)和自适应结构体(运行时自动发现 Map 中的列)。

http://www.cnnetsun.cn/news/2877360.html

相关文章:

  • 彻底搞懂:async/await 底层机制、Babel 编译原理与高阶业务避坑全参透
  • Android开发学习用代码包:从基础小例到完整项目,含模块化源码与详细说明
  • KOReader插件开发:从零开始打造你的电子书阅读器扩展
  • VS2015可直接编译的孙鑫MFC教学源码包,含命名管道、邮槽、MDI等IPC实战案例
  • DVR机箱有哪些类型?
  • 从零到一:手把手教你打造STC89C52RC最小系统板
  • 免费电子书管理神器:Calibre完整使用教程与30+格式转换指南
  • 3行代码解决复杂机器学习难题:AutoGluon自动化框架实战指南
  • 大模型之交互式应用(理论篇)
  • 基于内存补丁技术的企业级消息防撤回完整解决方案深度解析
  • 从 0 到 1 构建 WASM 应用:WebAssembly for .NET 开发实战指南
  • 3分钟解决Cursor试用限制:go-cursor-help终极指南
  • Netdisco与现有系统集成:如何与Zabbix、Nagios、Grafana等工具对接
  • PPBC植物图像库实战:如何用Python快速爬取并整理贵州常见灌木数据(以栎灌、小檗为例)
  • 从移动基站到固定网络:深入解析RTK与CORS的技术演进与应用分野
  • CVE-2026-41091漏洞详解:Microsoft Defender权限提升漏洞全面分析
  • R2 Bitcoin Arbitrager监控与报警:Slack和LINE实时通知配置指南
  • 大模型 Token 是什么?“词元”又是啥?—— 一篇让你彻底搞懂的“AI货币”指南
  • UE5 场景光影 实战调优指南
  • 遥感变化检测数据集全景解析:从经典基准到前沿应用
  • Harness Engineering:2026年大模型开发新趋势,小白程序员必备收藏指南!
  • Poppins字体终极指南:如何免费获得完美的多语言排版体验
  • Android计算机毕设之基于 SpringBoot 与 Android 的个人健康管理基于springboot+Android的健康管理应用的设计与实现(完整前后端代码+说明文档+LW,调试定制等)
  • C++音频开发实战:精选工具库与应用场景解析
  • 【AR隔空手势交互】Unity集成Manomotion SDK:从零到一的免费手势交互实践
  • MediAlbertina PT-PT 900M NER-openmind vs 传统模型:为什么它是葡萄牙医疗AI的终极选择?
  • 构建企业级API自动化测试平台的终极实战指南
  • 基于YOLOV5的区域选择目标检测与报警系统(代码+教程)区域目标检测 区域入侵检测
  • 企业微信群定时消息推送的自动化实现方案
  • 18.Isaac教程--坐标系:从像素网格到机器人运动的坐标统一