大规模数据OLAP分析爆内存问题分析与解决

大规模数据OLAP分析爆内存问题分析与解决

PR地址:Fix RocksDB OOM #823

问题背景

问题表现

在执行如下Gremlin语句时,如果一次查询分析的结果数据在千万条以上,进程内存在几分钟内暴涨,JVM老年代占据将近20G内存、RocksDB占用本地内存超过100G,最终导致OOM,甚至进程被系统kill掉。

g.V().hasLabel('vbsku').count() 
g.V().hasLabel('vbsku').groupCount().by(properties('p_trade').value())
问题分析:

经过分析,发现大量迭代器RocksIterator没有释放,达到几百万个。这些java层RocksIterator对象占用的内存其实并不算大,但是在JNI层之下,其持有的本地对象占用了大量资源。这些资源依赖JVM的GC垃圾回收机制来释放,当JVM GC压力大时,导致本地资源无法及时释放,最终恶性循环越积越多,内存撑爆。

那么,RocksIterator为何没有释放?

在HugeGraph中,一个用户查询Query会被拆分为多个子查询SubQuery,并且将SubQuery的结果通过迭代器Iterator列表串起来。每个SubQuery的的结果也是一个迭代器,比如顶点迭代器Iterator<Vertex>,边迭代器Iterator<Edge>;并且,最上层的接口到最底层的数据之间也是通过迭代器串联起来的,这中间可能涉及到针对迭代器的几类转换操作:

  • map
  • flatmap
  • batchmap
  • filter
  • concat

比如:

把二进制数据迭代器转换为顶点迭代器的操作:

  • map(Iterator<Entry>) => Iterator<Vertex>

或者把索引结果迭代器转换为顶点迭代器的操作:

  • map(Iterator<Entry>) => Iterator<Index>
  • batchmap(Iterator<Index>) => Iterator<IdList>
  • flatmap(Iterator<IdList>) => Iterator<Entry>)
  • map(Iterator<Entry>) => Iterator<Vertex>

这种获取数据的模式在数据库领域称之为火山模型,使用火山模型的目的是通过流式读取数据,而不是把所有数据都从磁盘读到内存(数据量比较大时内存装不下),再进行下一步操作。

由于迭代器的组合灵活,导致各类迭代器的生命周期难以管理,此前迭代器的释放基本是采取如下原则:

  1. 尽量在迭代器不需要使用的时候释放,比如对底层的数据迭代器,当取下一条数据next()发现已经无数据时,及时关闭。
  2. 对于无法及时关闭的的迭代器,则依赖JVM GC来自动释放,比如上层迭代到一半就因为其它条件不满足而提前结束时(类似limit()或one()等),底层迭代器无法感知,只能在无人引用该对象的时候,触发GC finalize()对底层迭代器进行释放。

正是因为有部分依赖GC机制释放的迭代器存在,引入了爆内存的风险,当GC压力大时,JVM本地冰山对象严重消耗内存,并且来不及回收,出现了上述问题。

问题解决

解决方案:
  1. 将所有的迭代器的生命周期进行链式管理,最上层的迭代器关闭时,传导到中间层的所有迭代器,最终传导到最底层的迭代器,触发资源释放。
  2. 所有迭代器,包括中间层和上层的,在无需继续使用时均采取手动关闭该迭代器,进而触发底层的迭代器释放。参考代码:Manually close the iterators
  3. 禁止使用一次性加载数据到内存集合的操作,把迭代器转换为集合,包括遗留的部分场景比如边的顶点列表获取、缓存列表获取、任务列表获取等等。参考代码:iter gloabal by batchlimit iterator to listforce limit BatchIdHolder.all()adapt ListIterator.list() return Collection
  4. 同时,解决了一些极端情况下爆内存的场景,比如超级点的边被大量加载到内存,限制了一次最多读取的条数。参考代码:fix cassandra oomfix check subRows().size() <= INLINE_BATCH_SIZE
  5. 顺便解决了offset在多个子查询结果之间无法定位的问题,思想是在最顶层的Query中维护一个偏移计数,从而让最底层的SubQuery也能够通过OriginQuery链共享该计数。参考代码:fix offset bug
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章