内存使用持续增加的原因

社区用户经常会反馈什么都没有做,但是内存持续升高。这里整理了几个解题思路:

  1. 确定到底什么进程占用过了过多的资源,top 看看到底 CPU、内存哪个进程占用高;
  2. 如果是 graph 的话,说明系统正在做查询,等查询完成之后看看是否资源占用会有所下降;
  3. 如果是其他服务,storage 会有一些固定的内存开销,比如:wal 会部分缓存在内存,这个会发生在有数据写入的时候,等数据完成写之后,占用情况会有所下降;此外,当系统在做 Compaction 时,也会导致内存上升;如果你设置的 partition 过多的话,又有可能会导致内存持续上升问题。

关于 storaged 内存占用讲解

以下内容来自 @xjc 的整理,感谢他的整理。

  1. storaged 的内存绝大部分是 RocksDB 的cache。另外一部分主要是 wal 的缓存,等于 wal_buffer_size * part个数wal_buffer_size 这个参数的默认值是 8M,这部分是固定的。

  2. RocksDB 的 cache 主要是两部分 block cache 和 bloom filter,其他 memtable 等占用小且固定;

    1. block cache 的大小由参数 rocksdb_block_cache 来决定,默认 4M,但通常建议为可用内存的 1/3 来提高性能。默认情况下,当有些数据被 pin 在 cache 中的时候,这个 cache 大小的设置可以被突破。block cache 内存占用可以通过以下 http 接口来查看:
    > http://\<ip\>:19779/rocksdb_property?space=space_name&property=rocksdb.block-cache-usage
    
    1. bloom filter 的内存占用默认情况下独立于 block cache,有两种配置 whole key 和 prefix,Nebula 2.6之前默认使用 whole key,2.6之后默认使用 prefix。Bloom filter 的内存占用可以通过以下http接口来查看:
    > http://\<ip\>:19779/rocksdb_property?space=space_name&property=rocksdb.estimate-table-readers-mem
    
    • whole key 的内存占可以用文档中给出的公式来测算,即点边数量 *15 bytes;
    • prefix 中主要是边的部分取决于图中点的出入度,相同点的相同类型的出入边会共用一个 prefix,因此节省内存占用。
  3. 使用参数 enable_partitioned_index_filter=true,可以将 bloom filter 进行分片存储以节省内存占用,并将 bloom filter 放入 block cache 中不占用额外内存,但是因为这个配置同时会将 RocksDB L0 的 filter pin 在内存中,根据上述第 2.1 点,实际内存占用会突破 rocksdb_block_cache 的配置,在大量导入数据且 auto compaction 关闭的情况下,由于数据集中在 L0,仍然会出现大量内存占用的情况。实践中,大数据量(TB级)情况下建议 auto compaction 打开,且通过增加 max_background_job 来提高 compaction 的性能。

其他参考贴

这里也收录了一些相关的调试 tips,可以参考下:

内容由 @wenhaocs 提供,感谢 :bouquet: ,原帖出处:nebula 日志报错 - #27,来自 wenhaocs

storaged 对内存的使用基本都来源于 rocksdb。对于读,rocksdb 对内存的使用主要是 block cache 和 page cache。对于写,对内存使用只有 memtable 和触发的 compaction。你的操作是 upsert,同时涉及读和写。

对读来说,block cache 大小通过 conf 里 rocksdb_block_cache 调节,rocksdb 可控。page cache 就是 OS 的 page cache,rocksdb 无法控制。因此如果 block cache size 设置合理的话,对于读,问题很可能出在 page cache 上。

对于写,如果存在大量的 update,可能触发大量 compaction,造成严重的写放大,造成 mem 使用率增高,磁盘 I/O bandwidth 使用率增高, 甚至不可用。

有以下可能的解决方法:

  1. conf 里 disable_page_cache 设置成 true 以关闭 page cache。我知道有些人会 argue 说利用操作系统的 page cache 在 block cache 不够的时候可以提高性能,但这个坏处以我的观点其实更大。因为第一 mem 不可控,第二完全可以通过增大 block cache 来解决。而且 FB 内部都是关闭 page cache 的。

  2. 如果你不愿意关闭 page cache,那么考虑其他优化的点。比如如果你的 SST file size 比较大,但是 key-value 又比较小,那么可能造成 index 数目太大。而 index blocks 默认不是在 block cache 里,会占用 page cache。因此可以考虑在 conf 里添加一项: --enable_partitioned_index_filter = false 以打开cache_index_and_filter_blocks in block cache。或者,也可以在 --rocksdb_db_options={} 添加 max_open_files=<some number> 来限制 index filter 的数目,但这可能会影响性能。

  3. 你可以检查 dashboard 里磁盘的性能指标,看看当时磁盘的 IO 情况。同时看看 storaged 的日志看看当时 compaction 情况。如果是 compaction 导致的内存占满的话,说明资源不够,建议添加磁盘, rebalance。

降低内存的小方法

这里收录社区小伙伴降低内存消耗的法子,记得给分享技能的小伙伴点个赞哟

如果你还有其他的调试内存的思路,欢迎在本帖留言哟~ 你将会获得一枚【调优员】徽章

2 个赞

stoarge 内存中相对大块的占用:

  1. RocksDB Block cache
  2. RocksDB Indexes and bloom filters (默认不会放入block cache中,可能会导致storage内存会超出预期)
  3. Raft buffer (每个part会占用少量内存 几十MB左右)

详情可参见 Memory usage in RocksDB · facebook/rocksdb Wiki (github.com)

内存回收机制上,占大头的 BlockCache 有最大容量限制,但由于 Index/Filter 以及其他 RocksDB 内部机制,最大容量限制一般是一个 soft threshold,并不是 hard limit。有效降低 Index/Filter 的影响机制就是上面所说的 enable_partitioned_index_filter=true

4 个赞

补充几点:

  1. 透明大页(THP)会使进程内存占用升高,建议将其关闭(never)或设为 advise。具体操作方法参考 Linux 内核相关文档。值得注意的是,在运行时关闭 THP,并不会立即使相关大页释放和替换,因此需要重启相关进程。
  2. Jemalloc 中的 cache 会使内存占用升高。第一,Jemalloc 会为每个线程分配 local cache,这部分内存占用量与 CPU 核心数、线程数正相关。第二,Jemalloc 在全局上也会 cache 一定量的内存。第三,Jemalloc 内部会因为内存碎片的原因,不得不 “cache” 多余的内存无法释放给 OS。

整体上,无论是这些原因,还是前文描述的 Block Cache 等因素,长期来看,进程的内存占用最终会稳定维持在一定水位,并在此基础之上随着压力(查询复杂度、并发度等)的起伏而升降。

当然,如果进程长期线性升高最终导致 OOM,就要考虑参数是否配置合理甚至存在内存泄漏了。

2 个赞

这里的wal是指raft wal,缓存最大占用内存其实是max_log_buffer_size * part个数,max_log_buffer_size的默认值是16M。另外这部分缓存只有在storaged重启以后才会释放。
@critical27 帮忙确认下。

max_log_buffer_size以及 wal_buffer_size可以自定义设置吗?

可以,storaged的配置。不过实际测试下来这两个参数没法完全限制住,将来可能会有新的fix,目前不建议在一个节点上有太多的分片。

根据CDH的建议,swap最好也关闭