strfry数据库设计原理:PackedEvent零拷贝编码+LMDB复合索引如何榨干查询性能
strfry数据库设计原理PackedEvent零拷贝编码LMDB复合索引如何榨干查询性能【免费下载链接】strfrya nostr relay项目地址: https://gitcode.com/gh_mirrors/st/strfrystrfry 是一个用 C 编写的高性能nostr relay中继服务器它的所有事件数据都存放在本地 LMDB 嵌入式数据库中不依赖 MySQL、PostgreSQL 等外部数据库。这篇文章带你彻底看懂它的数据库设计原理PackedEvent 零拷贝编码与LMDB 复合索引是如何让按作者/ID/标签/时间范围查事件这类查询快到几乎零开销的。如果你想知道一个开源 nostr relay 如何榨干查询性能答案就藏在这两套设计里。为什么 nostr relay 要自己设计数据库大多数应用会直接选一个通用数据库但 strfry 选择了一条更极客的路自研查询引擎 嵌入 LMDB 文件数据库src/apps/relay/。原因很简单查询模式是固定的。nostr 协议的查询就是 NIP-01 定义的过滤器字段ids、authors、kinds、tags、since、until、limit。既然查询类型有限就可以为每种类型预置最优索引做到几乎所有查询都有索引可用永远不走全表扫描。没有 SQL 就没有解析开销。所有查询计划在设计时就已确定没有 SQL 生成/解析也没有注入风险。读取路径零锁、零系统调用。LMDB 通过mmap映射进内存读取直接从页缓存拿数据多核并发下线性扩展。数据库的表结构和索引声明集中在 golpe.yaml 中用不到 50 行 YAML 定义了全部索引——这本身就是设计即文档的范例。PackedEvent88 字节定长头 变长 Tags 的零拷贝编码 每条 nostr 事件的完整 JSON 可能有几 KB但其中可被索引的信息其实只有很少一部分事件 ID、作者公钥、时间戳、kind、过期时间、以及索引化的 tag。strfry 把这些信息压进一个紧凑的二进制结构PackedEvent布局见 src/PackedEvent.h偏移字节字段长度说明0id32事件 SHA-256 摘要32pubkey32作者公钥64created_at8时间戳uint64 原样字节72kind8事件类型80expiration8过期时间戳88tags[]变长每个 tag 1 字节名称 1 字节长度 值这个设计的精妙之处固定 88 字节头部id、pubkey是定长 32 字节created_at/kind/expiration是定长 8 字节。取任意一个字段就是从偏移处切一段内存不需要反序列化。零拷贝读取核心类PackedEventView只是包了一个std::string_viewid()、pubkey()、kind()等方法直接返回缓冲区的子视图。从 LMDB 页缓存里读到的字节就是程序拿到的对象——中间没有任何解码、拷贝或堆分配。瘦身到极致签名、非索引字段在入库前全部剥掉原始 JSON 单独存放下文会讲。记录越小页缓存命中率越高这是小数据换大性能的典型取舍。tag 变长编码每个索引化 tag 只占1 1 值长度字节单 tag 值上限 255 字节遍历 tag 只需按长度步进同样零拷贝。LMDB 复合索引每个字段都夹带created_at 真正榨干性能的是 golpe.yaml 中Event表定义的那组复合索引。看这份索引清单索引名键结构特点created_at时间戳全库按时间排序的主扫索引id事件ID created_at按 ID 精确查、去重pubkey公钥 created_at按作者查kindkind created_at按类型查pubkeyKind公钥 kind created_at组合精确查tagtag名tag值 created_at多值索引每 tag 一条记录replace公钥 d-tag kind参数可替换事件去重deletion事件ID 公钥NIP-09 删除标记expiration过期时间戳定时清理注意规律除了极少数辅助索引每个业务字段的键后面都追加了 8 字节的created_at构建逻辑在 golpe.yaml 的indexPrelude中makeKey_StringUint64(packed.pubkey(), indexTime)。这就是复合索引的精髓LMDB 的 B 树按键的自然顺序排列键前缀相同的记录会物理连续存放且按时间戳递增。所以某作者在 1 月 1 日到 1 月 2 日之间的事件不再是先按作者捞出全部再逐条比对时间而是直接在键空间里定位一个连续区间——since/until过滤几乎免费。扫描器可以从until对应的上界直接开始反向扫遇到since立即停止中途还能随时挂起后文详述。由于created_at就在键里很多查询只需要读索引就能完成index-only scan连 88 字节的 PackedEvent 本体都不用碰。DBScan 查询引擎按过滤器自动挑选最优索引 有了索引怎么用最聪明答案是 src/DBQuery.h 中的DBScan引擎。它收到一个 nostr 过滤器后按一套简单启发式选择唯一最优索引有ids→ 用id索引ID 是 256 位摘要选择性最高。有tags→ 选值数量最少的 tag 组走tag索引。同时有authors和kinds且组合数 1000→ 走pubkeyKind复合索引。只有authors→ 走pubkey索引。只有kinds→ 走kind索引。都没有→ 兜底走created_at时间索引。多个过滤值则开多个扫描游标ScanCursor各自沿索引反向扫描把候选事件按created_at降序归并输出——保证新事件先发给用户符合 nostr 的交互直觉。更妙的是可挂起/恢复设计长查询不会霸占 CPU。每次扫描都消耗工作预算超过时间预算如 10ms就把游标位置resumeKeyresumeVal只有几百字节存下来排队让位给新查询下次轮到自己时从断点无缝继续。这意味着慢连接永远不会拖累整个 relay 的延迟而这一切不需要任何数据库线程池。原始 JSON 单独存放levId 是连接索引与正文的钥匙 索引值里存的不是事件本体而是一个自增主键levIdLocal Event ID。完整事件 JSON 存在另一张裸表EventPayload中键是levId值带一个类型字节0 原始 JSON1 zstd 字典压缩数据。这套索引与正文分离带来三个好处O(1) 正文定位命中索引后按levId一次整数键查找即可取回正文见 src/events.h 中的lookupEventByLevId。按需压缩默认明文存储保查询速度磁盘紧张时用strfry dict训练 zstd 字典后压缩正文索引不压缩、查询路径不受影响。去重与幂等插入时先查id索引判断是否重复天然实现幂等写入。再配合单写者线程的架构——relay 只有确认事件真正提交进 LMDB 之后才会向客户端回OKdurable writes写入路径也做到了不丢一条。性能收益小结设计点带来的收益PackedEvent 定长头 string_view读取字段零反序列化、零拷贝复合索引夹带created_atsince/until变成键空间连续区间扫描可提前终止索引与正文分离levId大量查询 index-only不碰正文正文可独立压缩DBScan 最优索引启发式每个查询只走一条最窄的索引路径扫描可挂起/恢复慢查询不阻塞新查询整体延迟稳定LMDBmmap 单写者读路径无锁写路径批量摊销 fsync想读源码关键文件清单 想亲手验证这些设计仓库git clone https://gitcode.com/gh_mirrors/st/strfry里这几个文件值得按顺序读src/PackedEvent.h—— 零拷贝编码的完整布局与读写 API不到 100 行建议第一个读。golpe.yaml—— 全部 LMDB 表、索引与键构建逻辑的声明。src/DBQuery.h——DBScan索引选择启发式与可挂起扫描游标。src/events.h/src/events.cpp—— 事件解析、验签、levId查找与写入。src/apps/relay/—— Writer、ReqWorker、ReqMonitor 等线程如何围绕这套数据库协作。README.md—— 架构总览Architecture 章节与本文互相印证。从 88 字节的紧凑编码到夹带时间戳的复合索引strfry 的数据库设计给所有查询模式已知的实时系统上了一课当你能放弃通用性时性能会自己找上门。【免费下载链接】strfrya nostr relay项目地址: https://gitcode.com/gh_mirrors/st/strfry创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Fast_Sentence_Embeddings性能揭秘:Cython与BLAS如何榨干CPU的每一滴算力

Fast_Sentence_Embeddings性能揭秘:Cython与BLAS如何榨干CPU的每一滴算力

Fast_Sentence_Embeddings性能揭秘:Cython与BLAS如何榨干CPU的每一滴算力 【免费下载链接】Fast_Sentence_Embeddings Compute Sentence Embeddings Fast! 项目地址: https://gitcode.com/gh_mirrors/fa/Fast_Sentence_Embeddings Fast_Sentence_Embeddings&…

2026/8/25 10:08:51 阅读更多 →
NewLife.Cube数据导入导出实战:Excel、CSV、JSON一键导出的配置技巧与常见坑

NewLife.Cube数据导入导出实战:Excel、CSV、JSON一键导出的配置技巧与常见坑

NewLife.Cube数据导入导出实战:Excel、CSV、JSON一键导出的配置技巧与常见坑 【免费下载链接】NewLife.Cube Web快速开发平台,搭建管理后台,灵活可扩展!内部集成了用户权限管理、模板继承、SSO登录、OAuth服务端、数据导出与分享等…

2026/8/26 11:58:34 阅读更多 →
字节极限的艺术:milliForth为何能从436字节压到340字节

字节极限的艺术:milliForth为何能从436字节压到340字节

字节极限的艺术:milliForth为何能从436字节压到340字节 【免费下载链接】milliForth A FORTH in 340 bytes — the smallest real programming language ever as of yet. 项目地址: https://gitcode.com/gh_mirrors/mi/milliForth milliForth 是一款仅 340 字…

2026/8/25 10:08:51 阅读更多 →

最新新闻

RP2350安全启动实战:从TrustZone到OTP密钥管理

RP2350安全启动实战:从TrustZone到OTP密钥管理

1. 为什么一颗MCU要专门聊安全:先说清楚威胁模型1.1 大多数嵌入式开发者的安全认知误区在做物联网产品、设备固件或者消费电子的时候,很多嵌入式工程师对"安全"的理解其实停留在非常表面的层次。最常见的想法是:我把固件编译好&…

2026/8/26 11:58:00 阅读更多 →
STM32 IIC通信实战:从原理到OLED驱动与调试排坑

STM32 IIC通信实战:从原理到OLED驱动与调试排坑

1. 项目概述:为什么IIC是嵌入式开发的必修课?如果你玩过STM32,或者任何一款单片机,肯定对IIC(Inter-Integrated Circuit)这个名词不陌生。它和SPI、UART并称为嵌入式开发的“三驾马车”,是连接各…

2026/8/26 11:58:00 阅读更多 →
智能体工作流新范式:用看板与Markdown实现可视化编排

智能体工作流新范式:用看板与Markdown实现可视化编排

1. 项目概述:当看板遇上文档,一种全新的智能体编排范式 如果你和我一样,在尝试用 Hermes Agent 这类智能体框架来构建自动化工作流时,常常会陷入一种纠结:一方面,我们习惯了用 Markdown 文件(比…

2026/8/26 11:58:00 阅读更多 →
构建个人离线漫画图书馆:从爬虫原理到自动化管理实践

构建个人离线漫画图书馆:从爬虫原理到自动化管理实践

1. 从在线追更到离线收藏:为什么你需要一个个人漫画图书馆作为一个看了十几年漫画的老读者,我经历过从租书店、报刊亭买漫画,到后来在各大在线漫画平台追更的整个变迁。方便是方便了,手指一划就能看,但问题也随之而来&…

2026/8/26 11:58:00 阅读更多 →
PHP休闲斗地主小游戏源码:自适应手机端与管理后端完整解析

PHP休闲斗地主小游戏源码:自适应手机端与管理后端完整解析

简介:在Web开发中,PHP凭借其轻量级、易部署的特点,常被用于搭建交互式游戏应用。以棋牌类游戏为例,通过请求-响应机制与AJAX轮询,即可实现回合制玩法,兼顾学习与实用价值。本文从技术架构出发,解…

2026/8/26 11:58:00 阅读更多 →
Spring Boot WebSocket消息推送服务:从连接管理到心跳保活实战

Spring Boot WebSocket消息推送服务:从连接管理到心跳保活实战

1. 项目概述:从零构建一个健壮的WebSocket消息推送服务最近在做一个后台管理系统的实时通知模块,需求很明确:当用户提交了工单、管理员处理了审批或者系统有重要公告时,需要立刻在用户的前端页面上弹出一个提示,而不是…

2026/8/26 11:56:59 阅读更多 →

日新闻

Python random 模块常用函数详解:从入门到实战

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要: 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数(random()、unifor…

2026/8/26 0:00:40 阅读更多 →
《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》读书笔记--第三章Databases and Database Files(2)

《Microsoft Sql server 2008 Internals》索引目录: 《Microsoft Sql server 2008 Internals》读书笔记--目录索引 在上篇文章中,主要介绍了创建数据库的基本语法和FileGroup的初步知识。需要注意的是: 关于FileGroup 如果你的系统是用Raid设备直接存…

2026/8/26 1:18:18 阅读更多 →
政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体怎么建?三种模式、三步路径与四个误区

政务AI智能体已经从概念试点阶段,转入了政务服务的常态化落地应用;在实际使用过程中,它能自主理解办事需求、辅助完成填报申报、开展材料预审,并联动多个系统协同作业,真正嵌入到政务办理的全流程当中。但在落地推进过…

2026/8/26 1:18:18 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/25 3:38:12 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/25 3:38:18 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/25 3:38:23 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/26 3:50:20 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/25 10:31:12 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/26 1:24:05 阅读更多 →