图数据库 NebulaGraph 的内存管理实践之 Memory Tracker

news2024/12/23 17:58:09

数据库的内存管理是数据库内核设计中的重要模块,内存的可度量、可管控是数据库稳定性的重要保障。同样的,内存管理对图数据库 NebulaGraph 也至关重要。

图数据库的多度关联查询特性,往往使图数据库执行层对内存的需求量巨大。本文主要介绍 NebulaGraph v3.4 版本中引入的新特性 Memory Tracker,希望通过 Memory Tracker 模块的引入,实现细粒度的内存使用量管控,降低 graphd 和 storaged 发生被系统 OOM kill 的风险,提升 NebulaGraph 图数据库的内核稳定性。

注:为了同代码保持对应,本文部分用词直接使用了英文,e.g. reserve 内存 quota。

可用内存

在进行 Memory Tracker 的介绍之前,这里先介绍下相关的背景知识:可用内存。

进程可用内存

在这里,我们简单介绍下各个模式下,系统是如何判断可用内存的。

物理机模式

数据库内核会读取系统目录 /proc/meminfo,来确定当前环境的实际内存和剩余内存,Memory Tracker 将“实际物理内存”作为“进程可以使用的最大内存”;

容器/cgroup 模式

nebula-graphd.conf 文件中有一个配置项 FLAG_containerized 用来判断是否数据库跑在容器上。将 FLAG_containerized(默认为 false)设置为 true 之后,内核会读取相关 cgroup path 下的文件,确定当前进程可以使用多少内存;cgroup 有 v1、v2 两个版本,这里以 v2 为例;

FLAG默认值解释
FLAG_cgroup_v2_memory_max_path/sys/fs/cgroup/memory.max通过读取路径确定最大内存使用量
FLAG_cgroup_v2_memory_current_path/sys/fs/cgroup/memory.current通过读取路径确定当前内存使用量

举个例子,在单台机器上分别控制 graphd 和 storaged 的内存额度。你可以通过以下步骤:

step1:设置 FLAG_containerized=true

step2:创建 /sys/fs/cgroup/graphd//sys/fs/cgroup/storaged/,并配置各自目录下的 memory.max

step3:在 etc/nebula-graphd.confetc/nebula-storaged.conf 添加相关配置

--containerized=true
--cgroup_v2_controllers=/sys/fs/cgroup/graphd/cgroup.controllers
--cgroup_v2_memory_stat_path=/sys/fs/cgroup/graphd/memory.stat
--cgroup_v2_memory_max_path=/sys/fs/cgroup/graphd/memory.max
--cgroup_v2_memory_current_path=/sys/fs/cgroup/graphd/memory.current

Memory Tracker 可用内存

在获取“进程可用内存”以后,系统需要将其换算成 Memory Tracker 可 track 的内存,“进程可用内存”与“Memory Tracker 可用内存”有一个换算公式;

memtracker_limit = ( total - FLAGS_memory_tracker_untracked_reserved_memory_mb ) * FLAGS_memory_tracker_limit_ratio

usable_memory

FLAG默认值解释支持动态改
memory_tracker_untracked_reserved_memory_mb50 MMemory Tracker 会管理通过 new/delete 申请的内存,但进程除了通过此种方式申请内存外,还可能存在其他方式占用的内存;比如通过调用底层的 malloc/free 申请,这些内存通过此 flag 控制,在计算时会扣除此部分未被 track 的内存。Yes
memory_tracker_limit_ratio0.8指定 Memory Tracker 可以使用的内存比例,在一些场景,我们可能需要调小来防止 OOM。Yes

这里来详细展开说下 memory_tracker_limit_ratio 的使用:

  • 在混合部署环境中,存在多个 graphd 或 storaged 混合部署是需要调小。比如 graphd 只占用 50% 内存,则需在 nebula-graphd.conf 中将其手动改成 0.5;
  • 取值范围:memory_tracker_limit_ratio 除了 (0,1] 取值范围外,还额外定义了两个特殊值:
    • 2:通过数据库内核感知当前系统运行环境的可用内存,动态调整可用内存。由于此种方式非实时,有一定的概率会感知不精准;
    • 3:limit 将被设成一个极大值,起到关闭 Memory Tracker 的效果;

Memory Tracker 的设计与实现方案

下面,讲下 Memory Tracker 的设计与实现。整体的 Memory Tracker 设计,包含 Global new/delete operatorMemoryStatssystem mallocLimiter 等几个子模块。这个部分着重介绍下 Global new/delete operator 和 MemoryStats 模块。

memory_tracker

Global new/delete operator

Memory Tracker 通过 overload 全局 new/delete operator,接管内存的申请和释放,从而做到在进行真正的内存分配之前,进行内存额度分配的管理。这个过程分解为两个步骤:

  • 第一步:通过 MemoryStats 进行内存申请的汇报;
  • 第二步:调用 jemalloc 发生真正的内存分配行为;

jemalloc:Memory Tracker 不改变底层的 malloc 机制,仍然使用 jemalloc 进行内存的申请和释放;

MemoryStats

全局的内存使用情况统计,通过 GlobalMemoryStats 和 ThreadMemoryStats 分别对全局内存和线程内部内存进行管理;

ThreadMemoryStats

thread_local 变量,执行引擎线程在各自的 ThreadMemoryStats 中维护线程的 MemoryStats,包括“内存 Reservation 信息”和“是否允许抛异常的 throwOnMemoryExceeded”;

  • Reservation

每个线程 reserve 了 1 MB 的内存 quota,从而避免频繁地向 GlobalMemoryStats 索要额度。不管是申请还是返还时,ThreadMemoryStats 都会以一个较大的内存块作为与全局交换的单位。

alloc:在本地 reserved 1 MB 内存用完了,才问全局要下一个 1 MB。通过此种方式来尽可能降低向全局 quota 申请内存的频率;

dealloc:返还的内存先加到线程的 reserved 中,当 reserve quota 超过 1 MB 时,还掉 1 MB,剩下的自己留着;

 // Memory stats for each thread.
 struct ThreadMemoryStats {
   ThreadMemoryStats();
   ~ThreadMemoryStats();
 
   // reserved bytes size in current thread
   int64_t reserved;
   bool throwOnMemoryExceeded{false};
 };
  • throwOnMemoryExceeded

线程在遇到超过内存额度时,是否 throw 异常。只有在设置 throwOnMemoryExceeded 为 true 时,才会 throw std::bad_alloc。需要关闭 throw std::bad_alloc 场景见 Catch std::bac_alloc 章节。

GlobalMemoryStats

全局内存额度,维护了 limit 和 used 变量。

  • limit:通过运行环境和配置信息,换算得到 Memory Tracker 可管理的最大内存。limit 同 Limiter 模块的作用,详细内存换算见上文“Memory Tracker 可用内存”章节;

  • used:原子变量,汇总所有线程汇报上来的已使用内存(包括线程 reserved 的部分)。如果 used + try_to_alloc > limit,且在 throwOnMemoryExceeded 为 true 时,则会抛异常std::bac_alloc

Catch std::bac_alloc

由于 Memory Tracker overload new/delete 会影响所有线程,包括三方线程。此时,throw bad_alloc 在一些第三方线程可能出现非预期行为。为了杜绝此类问题发生,我们采用在代码路径上主动开启内存检测,选择在算子、RPC 等模块主动开启内存检测;

算子的内存检测

在 graph/storage 的各个算子中,添加 try...catch (在当前线程进行计算/分配内存) 和 thenError (通过 folly::Executor 异步提交的计算任务),感知 Memory Tracker 抛出 std::bac_alloc。数据库再通过 Status 返回错误码,使查询失败;

在进行一些内存调试时,可通过打开 nebula-graphd.conf 文件中的 FLAGS_memory_tracker_detail_log 配置项,并调小 memory_tracker_detail_log_interval_ms 观察查询前后的内存使用情况;

folly::future 异步执行

thenValue([this](StorageRpcResponse<GetNeighborsResponse>&& resp) {
    memory::MemoryCheckGuard guard;
    // memory tracker turned on code scope
    return handleResponse(resp);
})
.thenError(folly::tag_t<std::bad_alloc>{},
    [](const std::bad_alloc&) {
    // handle memory exceed
})

同步执行

memory::MemoryCheckGuard guard; \
try {
    // ...
} catch (std::bad_alloc & e) { \
    // handle memory exceed
}

RPC 的内存检测

RPC 主要解决 Request/Response 对象的序列化/反序列化的内存额度控制问题,由于 storaged reponse 返回的数据均封装在 DataSet 数据结构中,所以问题转化为:DataSet 的序列化、反序列化过程中的内存检测。

序列化:DataSet 的对象构造在 NebulaGraph 算子返回结果逻辑中,默认情况下,已经开启内存检测;

反序列化:通过 MemoryCheckGuard 显式开启,在 StorageClientBase::getResponse's onError 可捕获异常;

错误码

为了便于分辨哪个模块发生问题,NebulaGraph 中还添加了相关错误码,分别表示 graphd 和 storaged 发生 memory exceeded 异常:

E_GRAPH_MEMORY_EXCEEDED = -2600, // Graph memory exceeded
E_STORAGE_MEMORY_EXCEEDED = -3600, // Storage memory exceeded

延伸阅读

  • 什么是 malloc 以及动态内存分配:https://en.wikipedia.org/wiki/C_dynamic_memory_allocation
  • jemalloc
    • 原始论文:https://www.bsdcan.org/2006/papers/jemalloc.pdf
    • Facebook 对 jemalloc 的优化:https://engineering.fb.com/2011/01/03/core-data/scalable-memory-allocation-using-jemalloc/

谢谢你读完本文 (///▽///)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/537211.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Amazon 一周回顾 – 2022 年 7 月 18 日

上周&#xff0c;Amazon 峰会&#xff08;纽约&#xff09;在贾维茨中心线下举办&#xff0c;有数千名与会者以及 100 多家赞助商和合作伙伴参加。在主题演讲中&#xff0c;Amazon 首席开发人员倡导者 Martin Beeby 谈到了云基础设施的创新如何帮助客户适应挑战并抓住新的机遇。…

开源之夏 2023 | 欢迎参加openEuler A-Tune SIG和Base-service SIG开发任务

开源之夏是中国科学院软件研究所联合openEuler发起的开源软件供应链点亮计划系列暑期活动&#xff0c;旨在鼓励在校学生积极参与开源软件的开发维护&#xff0c;促进优秀开源软件社区的蓬勃发展。活动联合各大开源社区&#xff0c;针对重要开源软件的开发与维护提供项目&#x…

linux Fd以及重定向讲解

感谢你的阅读&#xff0c;是对我最大的鼓励&#xff01;&#xff01;&#xff01;&#xff01; 目录 fd理解 文件操作重定向 让我们回顾C语言文件操作 首选我们要知道2个知识点&#xff1a; 额外知识点 如何理解一切皆文件呢&#xff1f; 当父进程fork创建子进程是否创建…

移动出行下半场,T3出行做起“加减法”

五一小长假外出聚餐时&#xff0c;在T3出行打了一辆网约车&#xff0c;车厢上居然印了《灌篮高手》的动漫角色。 松果财经了解到&#xff0c;这是T3出行在上海、深圳、广州、成都、重庆及武汉等六大城市与《灌篮高手》展开的合作&#xff0c;大概有数百辆定制网约车印上了《灌篮…

现在Java和大数据开发还能学来找工作吗?

卷是必然的&#xff0c;不止java和大数据&#xff0c;也不止IT这个行业~ 这两个方向其实都是不错的方向&#xff0c;java虽然卷&#xff0c;但是技能在手也不怕。大数据的发展前景也是不容小觑的。关键就在于你未来想发展的方向以及个人的兴趣 首先先给你吃一颗定心丸&#x…

HTML零基础快速入门(详细教程)

1&#xff0c;HTML代码特点 <html><head></head><body>hello world!</body> </html>HTML代码有以下特点&#xff1a; html代码是通过标签来组织的&#xff0c;而标签是由尖括号< >组织的&#xff0c;也可被叫作元素&#xff08;ele…

SpringCloudAlibaba项目打成war后无法注册到Nacos,无法连接到Nacos Server

SpringCloudAlibaba项目打成war后无法注册到Nacos,无法连接到Nacos Server 背景原因解决重新打包、部署&#xff1a;正常&#xff01; 参考&#xff1a;https://www.jb51.net/article/233940.htm 背景 正常jar(使用内嵌tomcat)包是可以连接并注册到Nacos的&#xff0c;但是将…

uniapp源码编译打包App上架发布详细教程

基础配置 App打包&#xff08;使用Hbuilder进行App打包&#xff09; 视频教程 视频地址&#xff1a; https://www.bilibili.com/video/BV1Kg411g7cb 一、修改接口地址 1.打开uni-app下config/app.js修改接口地址&#xff0c;将下图红框中的地址修改成您的域名 二、配置参…

研发工程师玩转Kubernetes——安装microk8s和Docker

《研发工程师玩转Kubernetes》将立足于实操&#xff0c;试图由浅入深探索Kubernetes世界。于是在技术选型和内容上也将由易到难&#xff0c;由简单到复杂。 为了降低进入的门槛&#xff0c;我们前期将不考虑多实例&#xff08;机器&#xff09;的部署&#xff0c;而采用单机部署…

ThreadLocal 的原理讲述 + 基于ThreadLocal实现MVC中的M层的事务控制

ThreadLocal 的原理讲述 基于ThreadLocal实现MVC中的M层的事务控制 文章目录 ThreadLocal 的原理讲述 基于ThreadLocal实现MVC中的M层的事务控制每博一文案1. ThreadLocal 给概述2. 抛砖引玉——>ThreadLocal3. ThreadLocal 的模拟编写4. ThreadLocal 源码原理分析5. Thre…

云厂商降价潮背后:来中小企业战场「拼刺刀」

如果说过往云厂商的降价打响的是从C端进军B端的营销战&#xff0c;那么在这一轮降价潮背后&#xff0c;对应的则是云厂商从大型KA客户向中小企业进军的信号&#xff0c;强被集成&#xff0c;强获客。 云厂商又一轮降价潮袭来。 5月16日&#xff0c;移动云宣布部分产品线最高降…

技术探秘:揭秘Bean Factory与FactoryBean的区别!

大家好&#xff0c;我是小米&#xff0c;一个热衷于技术分享的29岁小编。今天&#xff0c;我们来聊一聊在Spring框架中常用的两个概念&#xff1a;beanFactory和FactoryBean。它们虽然看似相似&#xff0c;但实际上有着不同的用途和作用。让我们一起来揭开它们的神秘面纱吧&…

SaleSmartly聊天机器人如何帮助您的电商业务(二)

上文为大家介绍了两类机遇SaleSmartly&#xff08;ss客服)强大的自动化编辑器构建的两类聊天机器人&#xff0c;接下来继续为大家介绍两种类型的聊天机器人。SaleSmartly&#xff08;ss客服&#xff09;可构建的聊天机器人还有以下两类&#xff1a; 3. NLP聊天机器人 SaleSma…

Datax+Datax-web2.1实现MySQL数据库数据同步(二)

目录 流程1、创建项目2、创建数据源3、创建任务模板4、构建任务5、执行同步任务 安装文档参考&#xff1a;https://blog.csdn.net/zhanremo3062/article/details/130728287 流程 这里以2.1.2版本为例&#xff0c;datax-web使用起来还是很顺滑的&#xff0c;操作流程跟其他任务…

结合自由能计算

结合自由能计算 打分函数 背景 打分函数广泛应用于基于结构的计算辅助药物设计&#xff0c;其通过定量化评估药-靶的相互作用为药物研发中的药效评估提供理论依据&#xff0c;提高活性化合物甄别的效率。定量评估药物与靶标蛋白的相互作用通常分为两步&#xff0c;一步是对接…

Android之 fragment页面碎片详解

一 简介 1.1 Fragment是Android3.0新增的概念&#xff0c;中文意思是“碎片”&#xff0c;它与Activity非常相似&#xff0c;是用来描述一些行为或者一部分用户界面 1.2 可以在一个单独的Activity中建立多个Fragment面板&#xff0c;也可以在多个Activity中复用Fragment 1.3…

【OpenCV】C++红绿灯轮廓识别+ROS话题实现

目录 前言 一、背景知识 Opencv轮廓检测 ROS相关知识 二、环境依赖 三、具体实现 Step1&#xff1a;初始化ROS&#xff0c;订阅话题 Step2&#xff1a;接收话题&#xff0c;进入回调 1. 帧处理 2. 膨胀腐蚀处理 Step3&#xff1a;红绿特征处理 1. 提取绘制轮廓 2…

20230517提升cv1826的打印等级

20230517提升cv1826的打印等级 2023/5/17 17:43 https://www.xitongjiaocheng.com/linux/2017/53494.html Linux内核log等级与printk打印消息控制 时间&#xff1a;2017-03-13 出处&#xff1a;系统之家复制分享人气(206次) 【大中小】 printk打印消息控制 // linux/includ…

认养农业小程序开发 赋予农业发展新模式

传统农业发展到今天&#xff0c;无论是规模还是方式都发生了很大的改变&#xff0c;尤其是在信息化时代影响下&#xff0c;智慧农业一步步发展起来&#xff0c;通过认养这种新模式与都市中想要体验农场乐趣的人群联系起来&#xff0c;既满足了都市人群体验农场生活乐趣的目的也…

720vr全景线上看车帮助企业快速对接到意向客户

360VR全景看车可以高精度还原线下展厅和各类汽车车型&#xff0c;进入VR汽车3d展厅&#xff0c;实现360度无死角VR看车&#xff0c;可以任意的查看和缩放&#xff0c;消费者根据自己的喜好更换车身的颜色&#xff0c;一键对比不同车型的外观、性能、耗能等&#xff0c;不用出门…