- 时序数据库
- 后端
- 数据存储
- 可观测性
- 云原生
【免费下载链接】mimir
Grafana Mimir provides horizontally scalable, highly available, multi-tenant, long-term storage for Prometheus.
本指南系统讲解 Grafana Mimir 仓库内置的listblocks工具——它作为mimirtool blocks list子命令实现,用于列出并展示指定租户(tenant)在对象存储中的 TSDB block 信息。你将掌握全部命令行参数、tabbed/json/yaml三种输出格式、GCS/Azure/S3 等后端配置方式,并从源码层面理解 block 元数据、deletion-mark / no-compact-mark 标记文件与并发读取等底层实现原理,可直接用于日常存储巡检、block 排查与运维脚本编写。
1. 工具定位:listblocks 是什么
listblocks是 Grafana Mimir 提供的一个对象存储巡检工具,作用是显示对象存储中某个租户所属 block 的元数据信息,包括 block ID、起止时间范围、时长、压缩级别、来源、标签、统计量(series/chunks/samples)、块大小等。
从仓库源码看,它并未作为独立二进制发布,而是注册在mimirtool的blocks命令族之下。pkg/mimirtool/commands/blocks.go中BlocksCommand注册了五个子命令:
list:列出并展示 block 详情(即本文主题 listblocks)mark:给 block 打标记copy:复制 blocksplit:拆分 blockundelete:撤销删除标记
因此命令行形态为mimirtool blocks list [flags],对应文档标题中的listblocks即该子命令背后的核心逻辑。
2. 命令行参数详解
list命令的所有 flag 在源码 pkg/mimirtool/commands/blocks/list/list.go 中注册,与原文档保持一致并补充了更精确的语义说明。
2.1 核心参数
| 参数 | 必填 | 默认值 | 说明 |
|---|---|---|---|
--user | 是 | — | 要列出 block 的用户(租户)ID,缺失时命令直接报错no user specified(见Validate()) |
--format | 否 | tabbed | 输出格式,必须为tabbed、json、yaml三者之一,非法值报错an invalid format was specified |
--show-deleted | 否 | false | 同时展示标记了删除(deletion-mark)的 block |
--show-labels | 否 | false | 展示 block 的外部标签(external labels) |
--show-ulid-time | 否 | false | 展示从 block ULID 中解析出的创建时间 |
--show-sources | 否 | false | 展示该 block 的压缩来源(compaction sources) |
--show-parents | 否 | false | 展示父 block(parents,即压缩前的源 block ULID 列表) |
--show-compaction-level | 否 | false | 展示压缩级别(compaction level) |
--show-block-size | 否 | false | 根据meta.json中的文件明细计算并展示 block 大小(可用时) |
--show-stats | 否 | false | 展示 block 统计量:series、chunks、samples 数量 |
--split-count | 否 | 0 | 非 0 时输出 split number,用于判断 split compaction 时 block 会归入哪个分片组 |
--min-time | 否 | 未设置 | 仅打印minTime >= 该值的 block |
--max-time | 否 | 未设置 | 仅打印maxTime <= 该值的 block |
--use-ulid-time-for-min-time-check | 否 | false | 为 true 时,ULID 时间早于--min-time的 block 不加载其meta.json,可大幅减少对象读取,但可能漏掉含未来时间戳数据的 block |
补充:源码中还有一个文档未提及的
--show-hints(展示压缩 hints),以及对象存储后端通用参数(见第 4 节)。源码注册的完整 flag 集合以 list.go 为准。
2.2 关键参数的行为细节
--split-count与--show-stats等:这些开关不仅控制"是否打印列",还控制输出内容。从 buildBlocksList 的实现看:
- 仅当
--split-count > 0时,输出splitID = HashBlockID(blockULID) % splitCount; - 仅当
--show-ulid-time时,输出ulidTime; - 仅当
--show-deleted时,输出deletionTime(由 deletion-mark 文件中的deletion_time转换而来); - 仅当
--show-stats时,输出numSeries、numSamples、numChunks; minTime、maxTime、duration、durationSeconds是无条件输出的基础字段。
--min-time/--max-time过滤:过滤在内存中完成——所有已加载的meta.json都会先读入,再按 block 的minTime/maxTime(毫秒时间戳)与过滤值比较决定是否输出(list.go)。因此这两个参数节省的是输出量而非读取量。
--use-ulid-time-for-min-time-check优化:该开关让过滤前置到"读取阶段"。在 pkg/util/listblocks/listblocks.go 的LoadMetaFilesAndMarkers中,遍历租户目录时如果ulidMinTime非零且block ULID 时间 < ulidMinTime,则直接跳过该 block 目录,不再读取其meta.json。由于 ULID 时间通常接近数据写入时间、且可能早于block 实际包含的数据时间范围(例如写入了未来时间戳的数据),所以文档明确警告:开启后"可能错误地跳过某些 block"。
3. 三种输出格式
默认tabbed格式面向人工阅读,无需借助其他工具;json与yaml格式便于机器解析,可配合jq/yq等工具做脚本化处理。三种格式输出的字段集合相同,仅呈现方式不同。
3.1 tabbed(默认)
Block ID Min Time Max Time Duration No Compact Size 01HRB9NDFKKYM8CKGPBEY0E8QX 2024-03-06T00:00:00Z 2024-03-07T00:00:00Z 24h0m0s [Time: 2025-04-10T19:48:56Z Reason: manual] 687 MiB 01HRDWWNZQCH1MWCWKK4VMW08R 2024-03-07T00:00:00Z 2024-03-08T00:00:00Z 24h0m0s 688 MiB输出由 Go 标准库text/tabwriter对齐(printTabbedOutput)。表头与数据行会依据启用的开关动态增删列:例如启用--split-count会增加Split ID列,启用--show-ulid-time增加ULID Time列,启用--show-deleted增加Deletion Time列,启用--show-stats增加Series/Samples/Chunks列,--show-labels/--show-sources/--show-parents/--show-hints分别追加对应列。No Compact列仅在 block 存在no-compact-mark.json时打印[Time: ... Reason: ...],否则留空。
3.2 json(为便于阅读此处做了美化缩进)
[ { "blockID": "01HRB9NDFKKYM8CKGPBEY0E8QX", "duration": "24h0m0s", "durationSeconds": 86400, "maxTime": "2024-03-07T00:00:00Z", "minTime": "2024-03-06T00:00:00Z", "noCompact": { "time": "2025-04-10T19:48:56Z", "reason": "manual" }, "size": "687 MiB", "sizeBytes": 720756845 }, { "blockID": "01HRDWWNZQCH1MWCWKK4VMW08R", "duration": "24h0m0s", "durationSeconds": 86400, "maxTime": "2024-03-08T00:00:00Z", "minTime": "2024-03-07T00:00:00Z", "size": "688 MiB", "sizeBytes": 721221809 } ]3.3 yaml
- blockID: 01HRB9NDFKKYM8CKGPBEY0E8QX duration: 24h0m0s durationSeconds: 86400 maxTime: "2024-03-07T00:00:00Z" minTime: "2024-03-06T00:00:00Z" noCompact: time: "2025-04-10T19:48:56Z" reason: manual size: 687 MiB sizeBytes: 720756845 - blockID: 01HRDWWNZQCH1MWCWKK4VMW08R duration: 24h0m0s durationSeconds: 86400 maxTime: "2024-03-08T00:00:00Z" minTime: "2024-03-07T00:00:00Z" size: 688 MiB sizeBytes: 721221809几点字段语义说明:
duration为人类可读时长(如24h0m0s),durationSeconds为向下取整的秒数;noCompact字段仅在存在 no-compact 标记时出现,内含标记时间与reason;size与sizeBytes仅在启用--show-block-size时出现,数值来自meta.json中thanos.files各文件字节数之和(GetBlockSizeBytes,见 pkg/util/listblocks/listblocks.go),以 1024 为基的humanize.IBytes格式输出;- 启用
--show-stats时额外出现numSeries/numSamples/numChunks,启用--show-labels时出现labels(map 形式),启用--show-sources时出现sources(ULID 数组),启用--show-parents时出现parents(父 block ULID 数组)。
4. 构建与运行:多对象存储后端示例
4.1 构建 mimirtool
list是 mimirtool 的子命令。在 Mimir 仓库根目录执行:
go build ./cmd/mimirtool也可以直接使用 Mimir 官方 release 提供的预编译二进制。构建完成后得到mimirtool可执行文件,即可按下面的示例调用。
4.2 后端通用参数
除list自身参数外,每个受支持的对象存储后端还会注入一批以--backend及相关前缀开头的通用参数。这些参数由bucket.Config.RegisterFlags(见 pkg/storage/bucket/client.go)及各后端自身的 flag 注册逻辑(S3 见 pkg/storage/bucket/s3/config.go)注册。核心通用项包括:
--backend:选择后端,如s3、gcs、azure、swift、filesystem;--storage-prefix:所有对象在存储中的公共前缀(仅允许数字与英文字母),Mimir 各组件写入对象时配置了该前缀,则巡检时也需保持一致。
4.3 Google Cloud Storage(GCS)
mimirtool blocks list \ --user <user> \ --backend gcs \ --gcs.bucket-name <bucket name> \4.4 Azure Blob Storage
mimirtool blocks list \ --user <user> \ --backend azure \ --azure.container-name <container name> \ --azure.account-name <account name> \ --azure.account-key <account key> \4.5 Amazon S3(含兼容服务)
mimirtool blocks list \ --user <user> \ --backend s3 \ --s3.bucket-name <bucket name> \ --s3.access-key-id <access key id> \ --s3.secret-access-key <secret access key> \ --s3.endpoint <endpoint> \补充说明(源码依据见 pkg/storage/bucket/s3/config.go):
--s3.endpoint既可以是 AWS S3 官方端点,也可以是 S3 兼容服务(如 MinIO)的hostname:port地址;- 若使用本地/S3 兼容测试环境,可加
--s3.insecure以http://访问端点; --s3.region未设置时客户端会发起GetBucketLocation自动探测;- 支持
--s3.native-aws-auth-enabled走 AWS SDK 默认凭据链,以及--s3.session-token临时凭证、--s3.signature-version签名版本等高级选项。
5. 源码级原理:listblocks 是如何工作的
5.1 整体流程
list子命令的执行入口在 Run:
- 校验
--user与--format(Validate); - 基于
bucket.Config创建对象存储客户端bucket.NewClient; - 调用
listblocks.LoadMetaFilesAndMarkers读取租户目录下的全部meta.json与标记文件; - 调用
buildBlocksList组装字段(过滤、开关判断、格式化); - 按
--format分别走printTabbedOutput(tabwriter)或json.Marshal/yaml.Marshal输出。
5.2 元数据与标记文件读取
核心读取逻辑在LoadMetaFilesAndMarkers(pkg/util/listblocks/listblocks.go):
- 先扫描
<user>/markers/目录,识别deletion-mark.json与no-compact-mark.json文件(listMarkerFiles); - 再迭代租户根目录,用
block.IsBlockDir识别 block 目录,跳过已标记删除(除非--show-deleted)和 ULID 时间早于--min-time(仅当开启--use-ulid-time-for-min-time-check)的目录; - 标记文件与
meta.json的读取均通过concurrency.ForEachJob以并发度上限 32执行(concurrencyLimit = 32),对象不存在(IsObjNotFoundErr)时静默跳过。
两个标记文件的 JSON 结构与语义定义在 pkg/storage/tsdb/block/markers.go:
DeletionMark:包含id、version、details与deletion_time(Unix 时间戳),表示 block 将在延迟后删除;NoCompactMark:包含id、version、details、no_compact_time与reason,表示 block 被排除在压缩之外。
reason的取值包括manual(人工指定)、block-index-out-of-order-chunk(索引含乱序 chunk)、critical(索引损坏等严重问题)、postings-offset-table-too-large、index-exceeds-64gib、symbol-table-too-large、preemptive(compactor 因接近结构上限而主动标记)。第 3 节示例输出中的Reason: manual即来自这类标记文件。
5.3 排序规则
输出前所有 block 会经过SortBlocks(pkg/util/listblocks/listblocks.go)排序,优先级依次为:
- 按
minTime升序; - 同
minTime时按时长(maxTime - minTime)升序; - 再按 compactor shard 标签(
__compactor_shard_id__)排序,先按分片总数、再按分片索引; - 最后按 ULID 时间升序。
该排序逻辑与 compactor 分片/压缩时的处理顺序保持一致,便于肉眼对比 block 之间的时间衔接关系。
5.4 block 大小的计算前提
--show-block-size依赖meta.json中的thanos.files条目(每个文件记录size_bytes)。若某个 block 的meta.json不含文件明细,则size输出为空字符串。因此"size 缺失"不代表 block 不存在,而可能是元数据不完整(如未完成压缩写入的中间状态)。
6. 实战场景示例
6.1 与 jq / yq 组合做脚本化巡检
以 JSON 输出配合jq筛选出某个时间窗口内的 block:
mimirtool blocks list \ --user tenant-a \ --backend s3 \ --s3.bucket-name mimir-tsdb \ --s3.endpoint s3.us-east-1.amazonaws.com \ --format json | jq '.[] | select(.minTime >= "2024-03-01T00:00:00Z" and .maxTime <= "2024-03-08T00:00:00Z")'6.2 排查压缩异常
同时开启--show-compaction-level、--show-sources、--show-parents,可以看清每个 block 由哪些源 block 压缩而来、当前处于第几级压缩;结合--show-deleted与默认输出的No Compact列,可快速定位被排除压缩或等待删除的 block:
mimirtool blocks list \ --user tenant-a \ --backend gcs \ --gcs.bucket-name mimir-tsdb \ --show-deleted \ --show-compaction-level \ --show-sources \ --show-parents6.3 大租户下的读取优化
对于 block 数量极大的租户,常规做法(先读全部meta.json再过滤)会产生大量对象读取请求。此时可先按--min-time划定时间窗,并开启--use-ulid-time-for-min-time-check,让工具在遍历目录时直接跳过 ULID 早于窗口的 block,显著降低对象存储请求量。需要谨记该开关可能漏掉"数据时间晚于 ULID"的未来数据 block。
7. 延伸阅读
- 命令实现:pkg/mimirtool/commands/blocks/list/list.go
- 读取与排序核心逻辑:pkg/util/listblocks/listblocks.go
- 标记文件结构与 reason 枚举:pkg/storage/tsdb/block/markers.go
- 对象存储后端通用配置:pkg/storage/bucket/client.go、pkg/storage/bucket/s3/config.go
- 命令族注册:
blocks下的mark/copy/split/undelete子命令见 pkg/mimirtool/commands/blocks.go
- 时序数据库
- 后端
- 数据存储
- 可观测性
- 云原生
【免费下载链接】mimir
Grafana Mimir provides horizontally scalable, highly available, multi-tenant, long-term storage for Prometheus.
相关推荐
Gallery 内置 Skill 实战:Mood Tracker 心情记录技能的协议设计、端侧存储与可视化实现
Gallery 内置 Skill 实战:Mood Tracker 心情记录技能的协议设计、端侧存储与可视化实现 本文以开源项目 gallery https://
时序数据库后端数据存储可观测性云原生gonum/plot完全指南:10分钟掌握Go语言数据可视化
gonum/plot完全指南:10分钟掌握Go语言数据可视化 gonum/plot是Go语言生态中功能强大的数据可视化库,提供了简洁直观的API用于创建各种高质
数据分析RITA与Zeek日志集成:从PCAP到威胁分析的全流程指南
RITA与Zeek日志集成:从PCAP到威胁分析的全流程指南 Real Intelligence Threat Analytics RITA 是一款强大的网络威
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考