1.背景
公司产业使用nebula图数据库的过程中总是遇到报错内存Used memory(178593436KB) hits the high watermark(0.800000) of total system memory(196268364KB), 导出无法执行数据操作
2.问题排查
查询官网博客发现解决办法是调整该参数 --system_memory_high_watermark_ratio = 0.9 ,默认为0.8,该参数的主要作用是判定系统使用内存占比超过80%时触发,导致数据库无法查询。那么在代码中是如何判断的,如下

3.具体分析
system_memory_high_watermark_ratio 是一个用于设置系统内存高水位线比例的标志(flag),在代码中主要用于判断系统内存的使用是否达到了设定的高水位线,以此来监控系统内存的使用情况,避免内存过度使用引发的问题。
在不同的场景下,system_memory_high_watermark_ratio 会被用于不同的判断逻辑:
- 在
MemoryUtils::hitsHighWatermark函数中,该比例用于判断当前系统内存的使用量是否超过了设定的高水位线。如果超过了,说明系统内存使用量已经达到了一个较高的水平,可能需要采取一些措施,如限制内存分配、记录日志或者抛出异常等。 - 在
Iterator::hitsSysMemoryHighWatermark函数和Executor::checkMemoryWatermark函数中,会根据MemoryUtils::kHitMemoryHighWatermark的值(该值是在MemoryUtils::hitsHighWatermark函数中根据system_memory_high_watermark_ratio判断后设置的)来决定是否抛出异常或返回错误状态,从而避免在内存使用过高的情况下继续执行可能会导致内存耗尽的操作。
代码中根据服务器的哪些信息去判断
代码中根据服务器的不同信息来判断内存使用是否达到高水位线,具体取决于服务器是否为容器化环境(通过 FLAGS_containerized 标志判断):
- 非容器化环境(
FLAGS_containerized为false)
在非容器化环境下,代码通过读取 /proc/meminfo 文件来获取系统内存信息:
FileUtils::FileLineIterator iter("/proc/meminfo", &reMemAvailable);
std::vector<uint64_t> memorySize;
for (; iter.valid(); ++iter) {
auto& sm = iter.matched();
memorySize.emplace_back(std::stoul(sm[2].str(), nullptr) << 10);
}
std::sort(memorySize.begin(), memorySize.end());
if (memorySize.size() >= 2u) {
total = memorySize.back();
available = memorySize[memorySize.size() - 2];
} else {
return false;
}
total:表示系统的总内存大小,通过读取/proc/meminfo文件并解析其中的信息,将最大的内存值作为总内存。available:表示系统当前可用的内存大小,通过读取/proc/meminfo文件并解析其中的信息,将第二大的内存值作为可用内存。
- 容器化环境(
FLAGS_containerized为true)
在容器化环境下,代码通过读取 cgroup 文件来获取内存信息:
bool cgroupsv2 = FileUtils::exist(FLAGS_cgroup_v2_controllers);
std::string statPath =
cgroupsv2 ? FLAGS_cgroup_v2_memory_stat_path : FLAGS_cgroup_v1_memory_stat_path;
FileUtils::FileLineIterator iter(statPath, &reTotalCache);
uint64_t cacheSize = 0;
for (; iter.valid(); ++iter) {
auto& sm = iter.matched();
cacheSize += std::stoul(sm[2].str(), nullptr);
}
std::string limitPath =
cgroupsv2 ? FLAGS_cgroup_v2_memory_max_path : FLAGS_cgroup_v1_memory_max_path;
auto limitStatus = MemoryUtils::readSysContents(limitPath);
NG_RETURN_IF_ERROR(limitStatus);
uint64_t limitInBytes = std::move(limitStatus).value();
std::string usagePath =
cgroupsv2 ? FLAGS_cgroup_v2_memory_current_path : FLAGS_cgroup_v1_memory_current_path;
auto usageStatus = MemoryUtils::readSysContents(usagePath);
NG_RETURN_IF_ERROR(usageStatus);
uint64_t usageInBytes = std::move(usageStatus).value();
total = static_cast<double>(limitInBytes);
available = static_cast<double>(limitInBytes - usageInBytes + cacheSize);
total:表示容器的内存限制大小,通过读取 cgroup 中的memory.max文件(cgroups v2)或memory.limit_in_bytes文件(cgroups v1)来获取。available:表示容器当前可用的内存大小,通过读取 cgroup 中的memory.current文件(cgroups v2)或memory.usage_in_bytes文件(cgroups v1)来获取,并加上缓存大小(通过解析memory.stat文件获取)。
4.最终判断逻辑
无论在容器化环境还是非容器化环境下,最终都会根据以下公式判断内存使用是否达到高水位线:
auto hits = (1 - available / total) > FLAGS_system_memory_high_watermark_ratio;
如果 hits 为 true,则表示系统内存使用已经达到了高水位线,会记录相应的日志信息。
浙公网安备 33010602011771号