Druid原理分析之内存池管理(转)

为了减轻JVM垃圾回收带来的性能波动,Druid尽量使用对外内存和系统内存。

  • 使用临时文件,在索引创建和合并的过程中,中间临时结果会占用大量的内存。为了减少JVM内存的使用,采用临时文件,通过文件IO的方式,巧妙地利用内核的Page Cache。为了提升IO的性能,Linux操作系统增加了Page Cahe。文件IO的写操作会写到Page Cache中立即返回,内核会定时将脏页也就是还没写到磁盘的页刷到磁盘中,但是并不会立即删除Page Cache,以便读取时快速访问。Apache Kafka也正是利用了文件IO这种特性。
  • MMap在Java中通过调用ByteBuffer的get/put接口来实现文件的读写。他也是依赖于内核的Page Cache。他最大的优势是直接操作内核的内存,减少一次内存复制。
  • DirectBuffer,在Druid中DirectBuffer的使用场景如下:

1. 在索引创建的过程中按块压缩/编码,LZF压缩除外,默认块大小为64KB。

2. 在索引查询过程中按块解压缩/编码,存放解压缩以后的数据,默认块大小为64KB。

3. 在查询过程中存放中间结果集。

4. Group By查询在上下文中设置“userOffHeap=true”,则使用DirectBuffer存放计算结果集。

Druid采用固定分区的内存池。固定分区的优点是足够简单,缺点是每次申请分配固定大小的内存,容易造成内存碎片。但根据上述的使用场景,编码/解码,压缩/解压缩都是按块操作的,所以固定分区非常切合这种场景。Group By查询需要根据维度拉取原始数据,然后在内存中进行聚合操作。默认的内存增量索引使用JVM内存。为了减少创建大量临时对象,Druid设计了临时计算结果集,采用固定大小的对外内存存储Metric的值,先在临时计算结果集中聚合。Druid会给每个处理线程从内存池中申请一块内存,内存大小通过buffer.sizeBytes设置。Group By查询采用固定大小的内存,会造成内存浪费。处理线程每次只处理一个segment分片,为了兼顾处理较大的Segment分片,我们一般将内存块的大小设置的大一些。这样处理较小的segment分片就会造成内存浪费。

猜你喜欢

转载自blog.csdn.net/mytobaby00/article/details/80071101