共计 2743 个字符,预计需要花费 7 分钟才能阅读完成。
摘要:华为云数据库 GaussDB(for Cassandra) 是一款基于计算存储拆散架构,兼容 Cassandra 生态的云原生 NoSQL 数据库;它依附共享存储池实现了强统一,保证数据的安全可靠。
本文分享自华为云社区《华为云数据库 GaussDB(for Cassandra)揭秘第二期:内存异样增长的排查经验》,原文作者:Cassandra 官网。
背景介绍
华为云数据库 GaussDB(for Cassandra) 是一款基于计算存储拆散架构,兼容 Cassandra 生态的云原生 NoSQL 数据库;它依附共享存储池实现了强统一,保证数据的安全可靠。外围特点是:存算拆散、低成本、高性能。
问题形容
GaussDB(for Cassandra)自研架构下遇到一些挑战性问题,比方 cpu 过高,内存透露,内存异样增长,时延低等问题,这些也都是开发过程中遇到的典型问题。剖析内存异样增长是一个比拟大的挑战,内存的异样增长对于程序来说是一个致命的问题,因为其可能触发 OOM,过程异样宕机,业务中断等后果,所以对内存进行正当的布局应用及管制就显得尤为重要。通过调整 cache 容量,bloom 过滤器大小,以及 memtable 大小等等,实现性能晋升,读写时延改善等成果。
在线下测试过程中发现内核在长时间运行后,内存只增不减,出现异常增长的状况,狐疑可能存在内存透露。
剖析 & 验证
首先依据内存应用,将内存分为堆内和堆外两个局部,别离进行该两块内存的剖析。确定有问题的内存是堆外内存,进一步对堆外内存剖析。引入更高效的内存管理工具 tcmalloc,解决内存异样增长问题。上面为具体分析验证过程。
确定内存异样区域
应用 jdk 的 jmap 命令和 Cassandra 的监控 (配置 jvm.memory.* 监控项) 等办法,每隔 1min 采集 jvm 的堆内内存及过程整体内存。
启动测试用例,直到内核的整体内存达到下限。剖析采集到的堆内内存和过程内存变动曲线,发现其堆内内存仍放弃绝对稳固,未呈现始终继续上涨,但期间内核的整体内存依然在继续上涨,两者的增长曲线不符。即问题应该产生在堆外内存。
堆外内存剖析验证
glibc 内存治理
应用 pmap 命令打印过程的内存地址空间散布,发现有大量的 64MB 的内存块和许多内存碎片,该景象与 glibc 的内存调配形式无关。堆外内存的应用和过程整体的内存增长趋势相近,初步狐疑该问题是由堆外内存导致。加之 glibc 偿还内存的条件刻薄,即内存不易及时开释,内存碎片多,猜想问题和 gblic 有关系。当内存碎片过多,闲暇内存节约重大,最终过程内存的最大使用量会呈现超过预期打算最大值的可能,甚至呈现 OOM。
tcmalloc 内存治理
引入 tcmalloc 内存管理器,代替 glibc 的 ptmalloc 内存治理形式。缩小过多的内存碎片,进步内存应用效率,本次剖析验证采纳 gperftools-2.7 源码进行 tcmalloc 的编译。运行雷同的测试用例,发现内存仍在继续上涨,然而上涨幅度较之前升高,通过 pmap 打印出该内存地址散布状况,发现之前的小内存块和内存碎片显著减小,阐明该工具有肯定优化成果,印证了后面提到内存碎片过多的猜想。
然而内存异样增长的问题依然存在,有点像是 tcmalloc 的回收不及时或者不回收导致。实际上 tcmalloc 的内存回收是比拟 “reluctant” 的,次要是为了当再次须要内存申请时能够间接应用,缩小零碎调用次数,进步性能。基于此起因,下来进行手动调用其开释内存接口 releasefreememory。发现成果不显著,起因临时未知(可能的确存在没待开释的闲暇内存)。
手动触发 tcmalloc 的 releasefreememory 接口
为验证该问题,通过设置 cache 容量的形式进行。
- 先设置 cache 的容量为 6GB,而后将读申请压起来,使 cache 的 6GB 容量填满
- 批改 cache 的容量为 2GB,为疾速是内存开释,手动调用 tcmalloc 的 releasefreememory 接口,发现没有成果,揣测采纳 tcmalloc 之后,内存依然始终上涨不上涨的起因可能与该接口的无关。
- 在 releasefreememory 接口外部的多个中央记录日志,而后启动过程再次测试,发现一处报错是在进行零碎调用 madvise 时有呈现失败。
- 代码地位:
报错日志信息:
- 通过该处的调用失败,剖析代码。发现 tcmalloc 的内存开释逻辑是“round-robin”,即两头有一个 span 开释失败,则后续待开释的 span 被终止,releasefreememory 逻辑调用完结。这个就和后面的景象吻合,执行完 releasefreememory 接口后根本没有成果,发现每次都是在开释了几十 MB 时,因为该接口的调用失败导致开释逻辑终止。
- 再次剖析该零碎调用 madvise 失败起因。通过给内核的该办法打 patch,发现其失败起因是因为传入的地址块对应的内存状态是 LOCKED 状态。导致系统调用失败,报错为非法参数。
- 内存为 LOCKED 状态,和该状态相干的有代码调用 mlock 零碎办法、零碎的 ulimit 配置。剖析相干代码未发现异常点。查问零碎 ulimit 配置,发现 max locked memory 为 unlimited。批改其配置为 16MB,重启 Cassandra 过程,再次测试,发现内存开释效果显著。
- 持续运行测试,发现内存继续上涨的状况隐没。在业务继续存在的状况下,内存会上涨到最高,不再上涨,放弃安稳,合乎内存打算使用量。业务压力缩小甚至进行后,内存呈现迟缓降落趋势。
解决 & 总结
- 引入 tcmalloc 工具,优化内存治理。比拟优良的内存管理器有 Google 的 tcmalloc 和 Facebook 的 jemalloc 等
- 批改零碎的 max locked memory 参数配置。
正当调配过程须要应用内存的最大值,并预留肯定容量,对于不合乎预期增长的内存须要进一步剖析。内存相干问题和程序相关性较强。零碎的要害配置需谨慎,要评估其影响。同时排查了相似的所有配置。
减少 releasefreememory 的命令,后端进行调用,优化 tcmalloc hold 内存不开释问题。不过 releasefreememory 命令的执行会锁整个 pageHeap,可能导致内存调配申请被 hang,所以须要小心执行。
后端减少可动静配置 tcmalloc_release_rate 的参数,来调整 tcmalloc 将内存交还给操作系统的频率。该值的正当范畴是[0-10],0 示意永远不交还,值越大,示意交还的频率越高,默认值是 1
结语
本文通过剖析开发过程中遇到的内存增长问题,应用更优良的内存管理工具,以及更细粒度的内存监控,更直观的监控数据库运行期间的内存状态,确保数据库安稳高性能运行。
点击关注,第一工夫理解华为云陈腐技术~