ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TiKV 全局内存分配器 tikv_alloc 指南:从 tcmalloc 编译选项到 jemalloc 内存剖析

TiKV 全局内存分配器 tikv_alloc 指南:从 tcmalloc 编译选项到 jemalloc 内存剖析 TiKV 全局内存分配器 tikv_alloc 指南从 tcmalloc 编译选项到 jemalloc 内存剖析【免费下载链接】tikvDistributed transactional key-value database, originally created to complement TiDB项目地址: https://gitcode.com/GitHub_Trending/ti/tikvTiKV 将全局内存分配器Allocator的选择与封装收敛在components/tikv_alloc这一个 crate 中开发者可以通过 cargo feature 或make环境变量在 jemalloc、tcmalloc、mimalloc、snmalloc 与系统分配器之间切换。本篇指南以 components/tikv_alloc/README.md 的用法为起点结合 crate 源码 与 Makefile 中的真实构建逻辑讲解如何为 TiKV 选择与编译内存分配器、如何开启 jemalloc 内存剖析以及tikv_alloc对外暴露的统计与追踪 API。读完本文你将掌握从换一个分配器重新编译 TiKV到用 jemalloc profiling 定位内存问题的完整操作路径。tikv_alloc 是什么为什么 TiKV 需要单独一个分配器 crateTiKV 是一个对内存分配行为高度敏感的分布式 KV 存储其进程内同时运行着 Raft 状态机、gRPC 服务、RocksDB 存储引擎以及大量后台线程。不同的分配器在碎片率、多线程扩展性、元数据开销上差异显著直接影响稳定性和性能。为此TiKV 将全局分配器这一横切关注点集中管理让所有二进制与测试在生产分配器下运行避免测试/基准与线上行为不一致为上层提供统一的统计与剖析接口屏蔽不同分配器 API 差异把分配器相关的unsafe与平台兼容逻辑限制在单一模块内符合其不希望 crate 外出现 jemalloc 专用代码的设计约束。正如 lib.rs 的文档注释所写该 crate 控制 TiKV 使用的全局分配器并在 Unix 上默认启用 jemalloc。由于 TiKV 库本身链接了tikv_alloc任何链接到 TiKV 的二进制都会自动获得 jemalloc反之不链接tikv的二进制如各组件测试、基准也应直接链接tikv_alloc以保证测试与生产使用同一分配器lib.rs。在源码层面全局分配器的注册位于 lib.rs#[global_allocator] static ALLOC: imp::Allocator imp::allocator();imp模块根据编译期 feature 与平台条件选择具体实现lib.rs条件使用的实现模块Unix 且非 fuzzing 且开启jemallocjemalloc.rs默认开启tcmalloctcmalloc.rs开启mimallocmimalloc.rs开启snmallocsnmalloc.rs以上均不满足system.rsstd::alloc::System注意条件组合中带有not(fuzzing)fuzzing是fuzz/cli.rs通过--cfg直接传给 rustc 的编译期标志不经过 cargo feature 体系lib.rs这是为了保证 fuzz 目标可复现而刻意绕开特定分配器。为 TiKV 选择并编译内存分配器方式一使用 cargo 直接构建tikv_alloc/README.md 给出的第一种用法是关闭默认 feature 并显式启用tcmalloc$ cargo build --no-default-features --featurestcmalloc需要说明的是tikv_alloc本身没有显式声明defaultfeature见 Cargo.toml--no-default-features是为了阻止上层 workspace 默认引入的jemalloc等 feature 与本次编译产生冲突。完整的 feature 清单如下Cargo.tomlfeature作用依赖jemalloc使用 jemallocUnix 默认tikv-jemallocator、tikv-jemalloc-ctl、tikv-jemalloc-sysmem-profiling以 profiling 能力编译 jemalloctikv-jemallocator/profilingtcmalloc使用 Google tcmalloctcmalloc0.3.0bundledmimalloc使用 Microsoft mimallocmimalloc0.1.39可选snmalloc使用 Microsoft snmallocsnmalloc-rs0.2可选在上层根 Cargo.toml 中这些 feature 被一一透传因此对tikv或tikv-server直接启用同名 feature 即可生效例如cargo build --features tcmalloc。tcmalloc 依赖采用bundled特性即构建时会自动编译内置的 tcmalloc C 源码无需系统预装。方式二通过 make 环境变量README 给出的第二种用法是通过 Makefile 的环境变量开关README 中原文为TCMALLOC1$ TCMALLOC1 make build这是日常开发中最常用的方式。Makefile 中分配器选择的实际逻辑如下# Pick an allocator ifeq ($(TCMALLOC),1) ENABLE_FEATURES tcmalloc else ifeq ($(MIMALLOC),1) ENABLE_FEATURES mimalloc else ifeq ($(SNMALLOC),1) ENABLE_FEATURES snmalloc else ifeq ($(SYSTEM_ALLOC),1) # no feature needed for system allocator else ENABLE_FEATURES jemalloc # Only tested on Linux ifeq ($(shell uname -s),Linux) ENABLE_FEATURES mem-profiling endif endif由此可以整理出完整的分配器选择矩阵环境变量效果备注不设置jemallocmem-profilingLinux 默认macOS 上仅 jemallocTCMALLOC1tcmalloc即 README 中示例MIMALLOC1mimallocSNMALLOC1snmallocSYSTEM_ALLOC1系统分配器不添加任何 feature值得注意的是默认构建并非裸 jemalloc而是带mem-profiling的 jemalloc在 Linux 上 Makefile 会额外追加mem-profilingfeatureMakefile。也就是说默认make build/make release出来的 TiKV 二进制本身就具备 jemalloc 剖析能力只是运行时默认关闭见下文运行时激活这与 README 中profiling 需要额外开启 feature的印象需要区分——若绕过 Makefile 直接用 cargo 构建则需显式追加--features mem-profiling。另外从 Makefile 可以看到默认还开启了-force-frame-pointers与-fno-omit-frame-pointerframe-pointer它保证基于 jemalloc profiling 的调用栈回溯足够可靠两者通常搭配使用。平台行为差异tikv_alloc 的 crate 级文档 明确说明了两点平台差异属于容易踩坑的边界并非所有平台都会覆盖 C mallocmacOS 上即使启用 jemallocC 层的malloc也未必被重定向这意味着 macOS 下 RocksDBC 代码可能仍然使用系统分配器而在 Linux 上 C malloc 会被重定向到 jemallocRocksDB 的内存也统一纳入 jemalloc 管理。所有 Unix 使用 jemalloc其他平台回退在非 Unix 平台如 Windows或 fuzzing 配置下imp落到system.rs使用std::alloc::Systemsystem.rs。构建时与运行时配置 jemalloc 内存剖析三步开启剖析编译期 feature、MALLOC_CONF、运行时开关完整的内存剖析需要构建期与运行期双重配置构建期让 jemalloc 编译进 profiling 代码。使用 Makefile 默认构建即可Linux 默认带mem-profiling或显式指定$ cargo build --features mem-profiling也可以对单个 crate 进行验证性构建$ cargo build -p tikv_alloc --features mem-profiling运行期通过 jemalloc 的MALLOC_CONF环境变量控制剖析行为。生产环境推荐的组合是打开prof编译进剖析支持但关闭prof_active先不实际采集等到出现内存问题时再动态激活$ export MALLOC_CONFprof:true,prof_active:false,prof_prefix:$(pwd)/jeprof运行期动态控制tikv_alloc提供了activate_prof()/deactivate_prof()/dump_prof(path)/set_prof_sample(rate)等 API见下文TiKV 的tikv-ctl与状态服务器即通过它们实现在线开关与导出。关于 Invalid conf pair 警告在设置了MALLOC_CONF之后运行cargo相关命令时控制台可能出现如下输出jemalloc: Invalid conf pair: prof:true jemalloc: Invalid conf pair: prof_active:falsecrate 文档 特别解释这是正常现象。该警告来自 cargo 与 rustc 自身内嵌的、未开启 profiling 的 jemalloc——它们读不懂prof:true这类剖析配置而 TiKV 自身链接的 jemalloc 只要以mem-profilingfeature 编译就能正确识别这些配置。因此看到警告并不代表配置失效。验证剖析是否生效tikv_alloc提供了一对检查函数jemalloc.rsis_profiling_enabled()读取opt.prof判断编译期是否已开启 profiling 支持is_profiling_active()读取prof.active判断运行期是否正在采集。对应地jemalloc.rs 中的测试 演示了完整的激活/停用/采样率往返流程且测试被标记为#[ignore #ifdef MALLOC_CONF]——只有当环境变量MALLOC_CONF存在时才会被测试运行器启用这一机制由 lib.rs 的自定义 test runner 实现避免在没有剖析配置的 CI 环境中误报失败。手动运行这些被忽略的测试$ MALLOC_CONFprof:true,prof_active:false cargo test --features mem-profiling -p tikv_alloc -- --ignored统一的内存统计 API无论用哪个分配器为了让上层代码如tikv-ctl、指标系统不感知分配器差异tikv_alloc对所有分配器暴露同一套 API。以 jemalloc 为参考实现其余分配器tcmalloc/mimalloc/snmalloc/system通过 default.rs 提供空实现或报错实现——例如dump_stats()返回空串、dump_prof()返回ProfError::MemProfilingNotEnabled从而保证接口签名一致。核心统计接口jemalloc.rsdump_stats() - String调用 jemalloc 的malloc_stats_print输出整体内存摘要并追加按线程聚合的alloc_bytes/dealloc_bytes明细fetch_stats() - ResultOptionAllocStats, Error先推进统计 epoch 刷新缓存再返回一组键值对。可获取的指标包括键名含义allocated当前已分配字节数active活跃页占用metadata分配器元数据开销resident常驻内存mapped映射内存retained保留未归还内存dirty由resident − active − metadata推导fragmentation由active − allocated推导碎片量线程级追踪依赖add_thread_memory_accessor()需在线程退出前配对调用remove_thread_memory_accessor()。其实现利用了 jemalloc 的thread.allocatedp/thread.deallocatedp返回的线程本地指针通过PeekableRemoteStat以无锁原子读的方式偷看其他线程的 TLS 计数jemalloc.rs从而在采集线程不干扰被采集线程的前提下汇总各线程分配/释放量。iterate_thread_allocation_stats在聚合时会用trim_thread_suffix去掉线程池线程名的数字后缀避免把同一线程池的每个线程拆成过多指标标签jemalloc.rs。内存追踪树按业务维度归因内存mem_trace除分配器原生统计外tikv_alloc还内置了一套与分配器无关的内存追踪树工具trace.rs。它不追求与调用栈一一对应而是按业务逻辑层级组织根节点之下划分 TiDB Endpoint、Transaction、Raft、gRPC 等组件各组件再按查询、store/apply 细分从而把谁吃了内存映射到业务语义。定义追踪树的宏mem_trace!(root, [(mid1, [leaf1, leaf2]), (mid2, [leaf3])])节点MemoryTrace维护原子计数与子节点表支持Add/Sub/Reset三种TraceEventtrace.rs。实用编程接口包括trace_guard(item, size)创建MemoryTraceGuard构造时自动Add(size)Drop或consume()时自动Sub(size)避免手工配对记账遗漏retrace(size)对象被替换为大小时调整记账值snapshot()/sum()导出快照或计算子树合计。trace.rs内附测试覆盖了宏展开的树结构求和、TraceEvent加减法语义以及MemoryTraceGuard的释放/重记账行为trace.rs可作为自定义业务维度内存追踪的参考样例。故障排查路径小结结合以上内容针对 TiKV 内存问题的排查可以按如下路径走通确认当前二进制使用的分配器默认 Linux 构建为 jemalloc含 mem-profiling通过TCMALLOC1 make build等可切换观察宏观指标通过fetch_stats()获取resident、mapped、dirty、fragmentation等维度判断是常驻膨胀、映射过多还是碎片问题定位线程维度dump_stats()输出各线程 alloc/dealloc 字节或通过iterate_thread_allocation_stats接入指标系统深挖调用栈设置MALLOC_CONFprof:true,prof_active:false,prof_prefix:...预埋剖析能力运行时调用activate_prof()开启采样、set_prof_sample(rate)调整采样粒度、dump_prof(path)导出 jeprof 文件再用 jeprof 工具分析热点分配路径业务归因如需按组件Raft/事务/gRPC拆分则使用mem_trace!构建追踪树通过snapshot()获取各业务节点的内存占用。上述 API 的完整调用方式与平台限制均可在 components/tikv_alloc/src 目录中直接查阅其中 lib.rs 是理解 feature 与平台选择的总入口jemalloc.rs 是剖析与统计能力的参考实现。【免费下载链接】tikvDistributed transactional key-value database, originally created to complement TiDB项目地址: https://gitcode.com/GitHub_Trending/ti/tikv创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表