当前位置:首页 > 文章列表 > Golang > Go教程 > Go Badger 怎么使用前缀迭代器读取一组记录

Go Badger 怎么使用前缀迭代器读取一组记录

来源:17golang原创 2026-10-13 16:57:18 0浏览 收藏

Go Badger 读取同一业务前缀下的一组记录,可以在只读事务中创建迭代器,用 Seek(prefix) 定位起点,再以 ValidForPrefix(prefix) 约束循环。需要把记录带出事务时,每条键和值分别用 KeyCopy(nil)、ValueCopy(nil) 复制,并在事务回调内关闭迭代器。下面用租户订单键说明这几个约束为什么要一起出现。

官方地址:https://github.com/dgraph-io/badger

订单列表为什么混入其他记录

假设业务把订单保存为 tenant:42:order:001、tenant:42:order:002,同一数据库还保存用户资料和其他租户的订单。页面只想列出租户42的订单,却使用 tenant:42 作为扫描前缀。这不是数据库识别错了租户,而是查询给出的字节范围比业务范围更宽。

前缀匹配不理解“租户编号”这个概念。tenant:420:order:001 同样以 tenant:42 开头;tenant:42:profile 也会匹配。把完整业务段及尾部分隔符纳入前缀,使用 tenant:42:order:,才能表达这个例子的订单集合。

先检查写入端与读取端是否使用同一套键格式,而不是急着调整预取参数。分隔符是键设计的一部分;若字段允许包含冒号,还需要约定编码或转义,不能只靠字符串拼接保证业务隔离。

Seek与循环条件分别约束什么

正向迭代时,Seek负责寻找目标键或其后的第一个键,ValidForPrefix负责检查当前位置是否仍属于目标集合。只调用Seek却用不带业务范围的Valid作为结束条件,尤其在没有设置Prefix选项时,可能继续读到后面的其他键。

IteratorOptions.Prefix 可以限定迭代范围;这里仍保留显式的ValidForPrefix,让循环本身清楚表达查询条件。默认选项作为起点,不开启Reverse或AllVersions,避免把逆序定位、历史版本读取混入这个基础例子。相关语义见 Badger迭代器接口 与 迭代选项。

订单业务前缀与迭代范围的静态关系
原创静态结构图:业务前缀、定位起点与循环边界的关系,不是运行截图。

在只读事务内返回独立记录

以下函数放在业务包中,接收调用方已经打开的数据库。它只做前缀查询,不负责打开、关闭数据库,也不解析订单内容。空前缀被主动拒绝,避免调用方漏传参数后变成无范围扫描。

package orders

import (
    "fmt"

    "github.com/dgraph-io/badger/v4"
)

// Record拥有独立的键和值副本。
type Record struct {
    Key   []byte
    Value []byte
}

// ReadPrefix只读取正向前缀集合,不返回失败时的部分结果。
func ReadPrefix(db *badger.DB, prefix []byte) ([]Record, error) {
    if db == nil {
        return nil, fmt.Errorf("数据库不能为空")
    }
    if len(prefix) == 0 {
        return nil, fmt.Errorf("查询前缀不能为空")
    }
    // 本次调用固定前缀;调用方仍不应并发修改输入。
    p := append([]byte(nil), prefix...)
    rows := make([]Record, 0)

    err := db.View(func(txn *badger.Txn) error {
        opts := badger.DefaultIteratorOptions
        opts.Prefix = p
        opts.PrefetchValues = true
        it := txn.NewIterator(opts)
        // 在事务回调结束前释放迭代器。
        defer it.Close()

        for it.Seek(p); it.ValidForPrefix(p); it.Next() {
            item := it.Item()
            key := item.KeyCopy(nil)
            value, err := item.ValueCopy(nil)
            if err != nil {
                return fmt.Errorf("读取键 %q 的值: %w", key, err)
            }
            // 每条记录使用独立切片,避免共享复用缓冲区。
            rows = append(rows, Record{Key: key, Value: value})
        }
        return nil
    })
    if err != nil {
        return nil, err
    }
    return rows, nil
}

这里刻意没有把Item指针存进结果。迭代器会复用Item,当前项不能被当作永久记录保存;Key、Value也有有效期限制。复制发生在当前迭代和事务内部,结果切片才不依赖随后Next或事务结束时的借用内存。接口说明见 Item文档。

另一个容易漏掉的细节是复用目标缓冲区:如果不断把同一个buf交给ValueCopy,再把返回切片直接追加到rows,多条记录可能共享底层数组。一次性收集全部记录时,传nil换取每条独立副本更容易维护。它会增加内存占用,不是零拷贝方案。

事务内Item和事务外Record的所有权关系
原创静态结构图:借用对象与独立结果副本的边界,不代表实测日志。

用相邻键检查结果边界

给读取函数传入 []byte("tenant:42:order:")。下面是供读者准备样本时使用的预期表,不是已经执行过的输出:

样本键应否返回原因
tenant:42:order:001是完整匹配订单前缀
tenant:42:order:002是同一集合中的另一个键
tenant:42:profile否业务段不同
tenant:420:order:001否租户段不同
tenant:43:order:001否相邻但不属于目标前缀

准备这些已提交的样本后,逐一比对结果键,再在函数返回后读取结果值,确认调用方拿到的是Record而不是Item。没有匹配记录时,函数应返回长度为零的切片和nil错误;读取某项失败时则应收到错误,不能把已收集的一部分当成完整订单列表。业务若希望允许部分结果,需要另行设计明确的完成标志,不能悄悄改变本函数契约。

键顺序是字节序,不是自然数字顺序。若订单号不补零,编号10可能排在编号2前面。这种顺序问题要在键编码或结果排序中解决,不能让Seek承担业务排序。

只读键和大结果集如何取舍

只做键清单、存在性判断等任务时,可把PrefetchValues设为false,并移除ValueCopy及值字段收集。这个开关表示不预取值,不表示以后禁止读取值;如果循环又调用ValueCopy,仍然会读取所需值。是否预取应取决于这次查询是否确实需要值,而不是一律关闭。

上述函数会把整个前缀集合保存在内存中。订单数量或值体积很大时,更合适的是带上限的查询或事务内逐条消费;逐条消费的回调也不能把借用切片直接留到事务外。上限查询只返回部分集合,应在接口中说明限制;它不是完整分页协议,更不能仅靠上一页长度判断数据再无变化。

读取期间不要关闭数据库,不要让多个goroutine共享同一个迭代器。数据库生命周期应由外层统一管理,而这里的defer只管理本次迭代器。

常见疑问与收尾检查

没有一条键等于prefix,Seek还有效吗?

有效。订单键通常比prefix更长,正向Seek能够找到其后的候选键;候选是否匹配,由循环的前缀条件判断。

没有匹配结果为什么不返回ErrKeyNotFound?

这是集合查询而不是单键Get。本函数把空集合视为正常结果;调用方可以依据len(rows)显示“暂无订单”。

把Reverse改成true就能倒着读取吗?

不能直接照搬这个定位方式。逆序Seek的定位方向不同,前缀本身不是该范围的最后一个键;需要单独设计上界和逆序起点。本文函数保持默认正向。

排查前缀扫描时,依次看键编码、定位起点、循环范围和结果所有权。完整业务前缀控制“读哪些”,KeyCopy与ValueCopy控制“如何保留”,迭代器关闭控制“何时释放”。把这三类约束分别写在对应位置,代码比依赖隐含约定更清楚。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go Badger 怎么给嵌入式键值数据设置过期时间Go Badger 怎么给嵌入式键值数据设置过期时间
上一篇
Go Badger 怎么给嵌入式键值数据设置过期时间
Go Badger 怎么批量写入并处理 Flush 错误
下一篇
Go Badger 怎么批量写入并处理 Flush 错误
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    460次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    522次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    541次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    488次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    307次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码