
KV存储嵌入式数据库【免费下载链接】pebbleRocksDB/LevelDB inspired key-value database in Go项目地址https://gitcode.com/gh_mirrors/pe/pebble点击查看免费下载Pebble 是一个用 Go 实现的 RocksDB/LevelDB 风格的键值存储引擎。当引擎出现性能问题时定位是谁发起了这次磁盘 I/O往往是解决问题的第一步是 WAL 同步、SSTable 刷盘还是后台 compactionLinux 内核自带的 block I/O 层 trace 能力为此提供了完美的答案。本文基于仓库中的 docs/io_profiling.md完整讲解perf与blktrace两套工具链的安装、录制、报告与解读方法并补充仓库源码证据帮助你读懂每一条 I/O 请求背后的代码来源。为什么需要 block I/O 层的性能剖析Linux 内核提供了极为丰富的性能剖析能力包括 CPU 性能计数器performance counters以及遍布内核各层级的 trace point跟踪点。其中block I/O 层即磁盘块设备读写路径的 trace 能力尤为强大可以精确记录每一次磁盘请求的设备号、扇区偏移、大小与操作类型。对于 Pebble 这类数据库引擎块 I/O 层的剖析数据有着直接的业务价值磁盘请求主要来自三类引擎内活动——WALwrite-ahead log预写日志同步、memtable 刷盘产生的 SSTable 写入以及后台 compaction。借助内核 trace可以精确量化哪个 Pebble 内部模块生成了多少磁盘请求例如定位到系统 96.76% 的块请求由 pebble 进程产生其中 85.58% 来自 WAL 同步。这为优化 fsync 频率、调整刷盘策略提供了直接的数据依据。文档定位提示本文是仓库内 docs/io_profiling.md 的扩展版。该文档整理了常用 Linux I/O 剖析菜谱recipes分为perf与blktrace两大部分以下内容沿用了这一主线并结合 record、vfs 等目录下的源码对剖析结果做了代码级印证。Perf用内核 trace point 定位块请求来源Linux 的perf命令既可以计量 CPU 性能计数器也可以挂载海量的内核 trace point。其两种操作模式分别是live 模式perf top实时显示当前热点record/report 模式perf record录制数据perf report/perf script离线分析。关键思路是录制block:block_rq_insert事件的栈回溯stack trace从而确定是哪一层的 Pebble 代码生成了块设备请求。block:block_rq_insert表示一个块请求被插入到设备的请求队列在 blk-mq 中对应blk_mq_insert_requests是判断谁发起了 I/O最合适的切入点。安装Ubuntu AWS 环境官方文档给出的 Ubuntu AWS 安装命令如下sudo apt-get install linux-tools-common linux-tools-4.4.0-1049-aws linux-cloud-tools-4.4.0-1049-aws注意linux-tools-*的版本号必须与当前内核版本匹配。在更现代的 Ubuntu 发行版上直接sudo apt-get install linux-tools-common并跟随提示安装对应内核版本的工具包即可也可以在安装后通过perf --version确认工具与内核的兼容性。文档示例中的4.4.0-1049-aws是当时 AWS 实例的内核版本并非固定要求。录制perf record 的关键参数perf record以及perf top需要读写/sys/kernel/debug/tracing的权限最简单的做法是以 root 身份运行。# Trace all block device (disk I/O) requests with stack traces, until Ctrl-C. sudo perf record -e block:block_rq_insert -ag # Trace all block device (disk I/O) issues and completions with stack traces, until Ctrl-C. sudo perf record -e block:block_rq_issue -e block:block_rq_complete -ag参数含义如下参数含义说明-aall CPUs在所有 CPU 上录制事件几乎总是期望的行为-gcall graphs同时录制调用栈栈回溯。这会让录制略微变贵但能确定事件的发起者栈回溯同时包含内核与应用代码从而可定位 I/O 是由 flush、compaction、WAL 写入等哪一类操作触发的-eevents指定被计量的事件。perf事件列表非常庞大可用sudo perf list查看-ooutput指定输出文件默认是perf.data关于事件选择block:block_rq_insert记录请求入队block:block_rq_issue记录请求下发到设备block:block_rq_complete记录请求完成。三者的组合可以分别刻画 I/O 的产生、下发与完成延迟。若希望只录制特定时长可追加-- sleep duration# Trace all block device (disk I/O) requests with stack traces for 10s. sudo perf record -e block:block_rq_insert -ag -- sleep 10报告perf report 与 perf script录制完成后perf.data可以用两个工具探索# Show perf.data in an ncurses browser. sudo perf report # Show perf.data as a text report. sudo perf report --stdio以perf record -e block:block_rq_insert -ag录制的数据为例perf report --stdio的输出大致如下96.76% 0.00% pebble pebble [.] runtime.goexit | ---runtime.goexit | |--85.58%-- github.com/cockroachdb/pebble/internal/record.NewLogWriter.func2 | runtime/pprof.Do | github.com/cockroachdb/pebble/internal/record.(*LogWriter).flushLoop-fm | github.com/cockroachdb/pebble/internal/record.(*LogWriter).flushLoop | github.com/cockroachdb/pebble/internal/record.(*LogWriter).flushPending | github.com/cockroachdb/pebble/vfs.(*syncingFile).Sync | github.com/cockroachdb/pebble/vfs.(*syncingFile).syncFdatasync-fm | github.com/cockroachdb/pebble/vfs.(*syncingFile).syncFdatasync | syscall.Syscall | entry_SYSCALL_64_fastpath | sys_fdatasync | do_fsync | vfs_fsync_range | ext4_sync_file | filemap_write_and_wait_range | __filemap_fdatawrite_range | do_writepages | ext4_writepages | blk_finish_plug | blk_flush_plug_list | blk_mq_flush_plug_list | blk_mq_insert_requests这段输出表明整个系统96.76%的块设备请求由pebble进程产生其中85.58%来自该进程内部的 WAL 同步WAL syncing。读懂这条调用链的仓库证据从栈回溯下半段ext4_writepages→blk_mq_insert_requests是内核侧将 dirty pages 写回磁盘的路径上半段则是 Go 应用侧。record.NewLogWriter.func2→LogWriter.flushLoop→flushPending→vfs.(*syncingFile).Sync→syncFdatasync正是 WAL 后台刷盘 goroutine 的完整调用链。对应的实现位于 record/log_writer.goNewLogWriter在初始化时会启动一个后台 goroutinego func() { pprof.Do(context.Background(), walSyncLabels, r.flushLoop) }()flushLooprecord/log_writer.go在循环中负责将完整的与部分的数据块刷写到底层 writer即 WAL 文件并在有 sync 请求时执行同步flushPendingrecord/log_writer.go执行具体的刷写与同步即调用syncingFile.Syncvfs.(*syncingFile).Syncvfs/syncing_file.go先通过ratchetSyncOffset原子更新同步水位再调用SyncData()在 Linux 上即fdatasync最终经syscall.Syscall进入内核。因此这一栈回溯是WAL fsync 是当前系统最大 I/O 来源的直接证据——对调优WALMinSyncInterval、WALBytesPerSync等选项极有参考价值。perf script原始事件数据的细节解读perf script可以访问原始请求数据。虽然它附带各种预录制的脚本但其主要用途是同时查看调用栈与 trace 数据。对块请求而言trace 数据会显示设备号、操作类型、偏移量与大小。# List all events from perf.data with recommended header and fields. sudo perf script --header -F comm,pid,tid,cpu,time,event,ip,sym,dso,trace ... pebble 6019/6019 [008] 16492.555957: block:block_rq_insert: 259,0 WS 0 () 3970952 256 [pebble] 7fff813d791a blk_mq_insert_requests 7fff813d8878 blk_mq_flush_plug_list 7fff813ccc96 blk_flush_plug_list 7fff813cd20c blk_finish_plug 7fff812a143d ext4_writepages 7fff8119ea1e do_writepages 7fff81191746 __filemap_fdatawrite_range 7fff8119188a filemap_write_and_wait_range 7fff81297c41 ext4_sync_file 7fff81244ecb vfs_fsync_range 7fff81244f8d do_fsync 7fff81245243 sys_fdatasync 7fff8181ae6d entry_SYSCALL_64_fastpath 3145e0 syscall.Syscall 6eddf3 github.com/cockroachdb/pebble/vfs.(*syncingFile).syncFdatasync 6f069a github.com/cockroachdb/pebble/vfs.(*syncingFile).syncFdatasync-fm 6ed8d2 github.com/cockroachdb/pebble/vfs.(*syncingFile).Sync 72542f github.com/cockroachdb/pebble/internal/record.(*LogWriter).flushPending 724f5c github.com/cockroachdb/pebble/internal/record.(*LogWriter).flushLoop 72855e github.com/cockroachdb/pebble/internal/record.(*LogWriter).flushLoop-fm 7231d8 runtime/pprof.Do 727b09 github.com/cockroachdb/pebble/internal/record.NewLogWriter.func2 2c0281 runtime.goexit解读 trace 数据字段对perf script输出中的事件行做逐字段拆解259,0 WS 0 () 3970952 256 | | | | | | | size (sectors) | | | | | offset (sectors) | | | - flags: R(ead), W(rite), B(arrier), S(ync), D(iscard), N(one) | - device: major, minor上面这条记录表示从扇区3970952开始执行了一次256扇区的同步写入。扇区大小sector size取决于设备可以用blockdev --report device查询但几乎总是512字节。本例扇区大小为512字节意味着这是一次128 KB的写入256 × 512 字节 131072 字节。对比perf report的调用链这条perf script记录再次印证该 128KB 同步写来自 WAL 后台刷盘 goroutineNewLogWriter.func2 → flushLoop → flushPending → syncingFile.Sync → syncFdatasync其内核侧路径正是ext4_writepages经由 plug 机制批量下发请求——这也解释了为什么记录中会出现blk_flush_plug_list/blk_mq_insert_requests等内核帧。Blktrace块层专用的 I/O 追踪blktrace记录的数据与perf类似但它专门面向块层block layer而不是通用目的。职责划分如下blktrace负责录制数据blkparse负责解析并显示数据btrace一个快捷命令相当于把blktrace的输出直接管道给blkparse。安装Ubuntu AWS 环境sudo apt-get install blktrace基本用法# Pipe the output of blktrace directly into blkparse. sudo blktrace -d /dev/nvme1n1 -o - | blkparse -i - # Equivalently. sudo btrace /dev/nvme1n1blktrace -d device指定追踪的块设备-o -将原始 trace 输出到 stdout 以便直接管道给blkparse -i --i -表示从 stdin 读取。btrace则一行搞定同样的事情。输出格式解读blktrace捕获的信息与perf类似示例输出如下sudo btrace /dev/nvme1n1 ... 259,0 4 186 0.016411295 11538 Q WS 129341760 296 [pebble] 259,0 4 187 0.016412100 11538 Q WS 129342016 40 [pebble] 259,0 4 188 0.016412200 11538 G WS 129341760 256 [pebble] 259,0 4 189 0.016412714 11538 G WS 129342016 40 [pebble] 259,0 4 190 0.016413148 11538 U N [pebble] 2 259,0 4 191 0.016413255 11538 I WS 129341760 256 [pebble] 259,0 4 192 0.016413321 11538 I WS 129342016 40 [pebble] 259,0 4 193 0.016414271 11538 D WS 129341760 256 [pebble] 259,0 4 194 0.016414860 11538 D WS 129342016 40 [pebble] 259,0 12 217 0.016687595 0 C WS 129341760 256 [0] 259,0 12 218 0.016700021 0 C WS 129342016 40 [0]标准格式为device cpu seqnum timestamp pid action RWBS start-sector size [command]字段含义字段含义device块设备主/次设备号如259,0cpu事件发生的 CPU 编号seqnum该 CPU 上的事件序列号timestamp事件时间戳秒pid发起进程的 PIDaction动作类型例如Q入队、G获取请求、U解除 plug、I插入、D下发、C完成各 action 的完整说明参见man blkparseRWBS读/写标志与屏障、同步等属性W 写、S 同步含义同 perf 的 flags 解析start-sector起始扇区号 size请求长度扇区数[command]发起命令的进程名此处为pebble以输出中的一行259,0 4 186 0.016411295 11538 Q WS 129341760 296 [pebble]为例pid 11538 的 pebble 进程在 CPU 4 上把一个296扇区约 148 KB的同步写请求插入块层队列。动作链的阅读方法行首Qqueued→Ggetting request→Uunplug→Iinserted→Dissued下发到设备→Ccompleted展示了同一个 I/O 在块层生命周期中的各个阶段C行的 pid 为0是因为完成中断可能由任意 CPU 处理不再归属发起进程。用 blktrace 发现异常 I/O 模式blktrace输出可用于凸显有问题的 I/O 模式。例如可以据此判断是否存在过多的小型顺序读I/O从而说明动态预读dynamic readahead工作不正常。实际排查时的观察要点顺序读但请求过小且密集例如大量R 129341760 84 KB级别的连续扇区请求说明每次仅预读了很小的窗口预读效率低下大量同步写WS说明应用层 fsync/fdatasync 过于频繁可对照perf的栈回溯确认来源是否为 WAL 刷盘如 record/log_writer.go 的flushLoopUunplug频繁出现说明块层 plug 机制生效请求以批量形式下发若I与D之间的时间间隔很大则可能意味着 I/O 调度器排队严重。将剖析结果与 Pebble 配置联动块层剖析的根本目的是指导引擎配置调整。以下是文档中未展开、但可直接从剖析结论映射到仓库配置项的对照表配置定义见 options.go剖析发现对应配置项说明大量 WALfdatasync栈顶为syncingFile.syncFdatasyncWALMinSyncIntervaloptions.go两次 WAL 同步之间的最小间隔过快的同步请求会被人为延迟默认约 500µs 的人工小延迟用于批量合并同步希望 WAL 周期性后台同步以平滑延迟WALBytesPerSyncoptions.go写入 WAL 的字节数达到该值后在后台调用Sync默认0即不做后台同步与 RocksDB 默认行为一致SSTable 写入触发大面积 writebackBytesPerSyncoptions.go周期性同步 SSTable 以平滑磁盘写入默认 512KB不提供持久性保证仅避免 OS 一次性刷出大量脏缓冲导致的延迟尖峰这些配置经由 open.go 组装成LogWriterConfig传入record.NewLogWriter最终由 vfs/syncing_file.go 的NewSyncingFile包装底层文件实现周期同步。值得注意的是vfs/syncing_file.go 的maybeSync实现刻意避免同步文件末尾 1MB 的数据syncRangeBuffer 1 20并做 4KB 对齐以减少重复改写同一页并避开 OS 写回时的阻塞——这是剖析输出中 periodic sync 类请求的代码来源。小结perf通用 CPU/trace point 剖析器。用perf record -e block:block_rq_insert -ag录制块请求栈回溯用perf report定位 I/O 来源模块如 WAL flushLoop用perf script --header -F ...查看每个请求的设备号、扇区偏移与大小。blktrace/btrace/blkparse块层专用工具。sudo btrace /dev/device一行即可实时观察请求的入队Q、下发D、完成C全过程适合检查请求大小分布、顺序性以及预读效率。解读要点先看 trace 数据的device、flags、offset、size再结合调用栈判断 I/O 来自 flush、compaction 还是 WAL 同步最后将结论映射到WALMinSyncInterval、WALBytesPerSync、BytesPerSync等引擎配置上。两套工具互为补充perf擅长谁发起的blktrace擅长设备上发生了什么。对于以 Pebble 为存储引擎、追求可预测低延迟的服务定期用这两条菜谱复盘磁盘行为是避免 writeback 风暴与 fsync 抖动的最直接手段。赞分享KV存储嵌入式数据库【免费下载链接】pebbleRocksDB/LevelDB inspired key-value database in Go项目地址https://gitcode.com/gh_mirrors/pe/pebble点击查看免费下载相关推荐Linux磁盘I/O性能深度剖析从/proc/diskstats到内核源码实现Linux磁盘I/O性能深度剖析从/proc/diskstats到内核源码实现 你是否曾因磁盘性能问题导致应用响应缓慢而苦恼面对 /proc/disksta操作系统内核驱动驱动开发虚拟化嵌入式网络存储Linux磁盘I/O性能分析终极指南iosnoop工具完整教程Linux磁盘I/O性能分析终极指南iosnoop工具完整教程 Linux系统性能优化中磁盘I/O延迟分析是每个系统管理员和开发人员必须掌握的技能。perf可观测性运维Linux内核异步I/O框架io_uring vs AIO性能对比Linux内核异步I/O框架io_uring vs AIO性能对比 引言异步I/O的性能困境与解决方案 在高性能服务器开发中I/O操作的效率直接决定应用性操作系统内核驱动驱动开发虚拟化嵌入式网络存储上一篇StreamController 项目教程下一篇Rufus 制作启动U盘完整指南5~15 分钟做出可用的 Windows 11 安装盘创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考