1. 现状

metadata模块内部使用MNode实现元数据树结构,并在内存中全量存储元数据信息。

外部模块与MManager交互时,使用PartialPath、MeasurementPath等对象装载元数据信息。

PartialPath、MeasurementPath是元数据树基于MNode信息实时生成的通信对象。

2. 问题

2.1. 单任务场景

任何一个任务访问MManager获取元数据信息时,所有的通信对象都是实时生成的,因此实时生成的对象会短暂占用内存。

在某实际场景中,IoTDB通过180G内存管理1.2亿的序列,合并模块一次元数据请求会造成12G的临时内存开销,均为实时生成的PartialPath。

2.2. 多任务场景

单任务执行时会有造成临时的内存负载,在多任务场景下,内存负载会被放大。

由于PartialPath等通信对象都是实时生成,因此每个任务线程都持有自身从MManager获取的实时生成对象。

即元数据信息被每个任务线程进行了拷贝,内存开销相较于单任务场景线性增长。


3. 方案

3.1. PartialPath瘦身

PartialPath目前为了与TsFile交互的方便,直接继承了TsFile的Path类,导致PartialPath内部信息过于冗余。

因此考虑解除PartialPath对Path的继承,在与TsFile交互时实时生成。


收益:单个PartialPath的大小可减少24字节以上


3.2. PartialPath享元

线程使用PartialPath时均以强引用方式使用。

PartialPath存储时,在MNode设置弱引用的partialPath字段。

每一次获取partialPath时,优先获取当前弱引用指向的对象。

如果弱引用指向null,则表明此时该MNode的partialPath未被任何线程使用,因此需要生成PartialPath对象。

public class MNode{
    WeakReference<PartialPath> partialpath = null;
    
    public String getFullPath(){
        return getPartialPath().getFullPath();
    }
    
    public PartialPath getPartialPath(){
        PartialPath partialPath = this.partialPath.get();
        if(partialPath == null){
            synchronized(this){
                if((partialPath = this.partialPath.get()) == null){
                    partialPath = this.concatPartialPath();
                    this.partialPath = new WeakReference(partialPath);
                }
            }
        }
        return partialPath;
    }
}


收益:多任务并行时,无同一逻辑PartialPath多实例问题


3.3. MetadataQueryUnit抽象

对通信对象进行抽象,建立接口MetadataQueryUnit,利用MNode实现MetadataQueryUnit。

MetadataQueryUnit是一种元数据信息的访问代理,基于这个代理可以控制外部的视图和访问权限。

外部需要使用PartialPath时,实时从MetadataQueryUnit中获取,MetadataQueryUnit利用MNode生成PartialPath。

虽然MNode中的PartialPath仍是实时生成,但是这种延迟生成可大幅缩短PartialPath的生命周期,进而降低内存开销的瞬时峰值。

对外部暴露的接口示例如下(仅列举PartialPath相关内容,针对查询的业务MetadataQueryUnit将会实现更多职责):

public interface IMetadataQueryUnit{
    
    PartialPath getPartialPath();
    
    ...
}
​
public interface IStorageGroupQueryUnit extends IMetadataQueryUnit{
    
    long getDataTTL();
    
    ...
}
​
public interface IMeasurementQueryUnit extends IMetadataQueryUnit{
    
    IMeasurementSchema getMeasurementSchema();
    
    ...
}
​
public interface IAlignedMeasurementQueryUnit extends IMetadataQueryUnit{
    ...
}
​


根据元数据内部的具体实现,MetadataQueryUnit的实现也需多种适配方案。

例如MTree上存储在的MNode,可直接用于一种实现;Template表征的序列不存在MNode实例,则需要具体的信息来实现。

public class MetadataQueryUnit implements IMetadataQueryUnit{
    
    IMNode node;
    
    PartialPath getPartialPath(){
        return node.getPartialPath();
    }
    
}
​
public class MetadataInTemplateQueryUnit implements IMetadataQueryUnit{
    
    PartialPath partialPath;
    
    PartialPath getPartialPath(){
        return partialPath;
    }
    
}
​
public class StorageGroupQueryUnit implements IStorageGroupQueryUnit{
    
    IStorageGroupMNode storageGroupMNode;
    
    PartialPath getPartialPath(){
        return storageGroupMNode.getPartialPath();
    }
        
    long getDataTTL(){
        return storageGroupMNode.getDataTTL();
    }
    
}
​
public class MeasurementQueryUnit implements IMeasurementQueryUnit{
    
    IMeasurementMNode measurementMNode;
    
    PartialPath getPartialPath(){
        return measurementMNode.getPartialPath();
    }
    
    IMeasurementSchema getMeasurementSchema(){
        return measurementMNode.getSchema();
    }
    
}
​
public class MeasurementInTemplateQueryUnit implements IMeasurementQueryUnit{
    
    PartialPath partialPath;
    
    IMeasurementSchema measurementSchema;
    
    PartialPath getPartialPath(){
        return partialPath;
    }
    
    IMeasurementSchema getMeasurementSchema(){
        return measurementSchema;
    }
    
}


收益:单个MetadataQueryUnit仅包含一个MNode引用,其存储开销远小于PartialPath,进而单次的元数据交互的瞬时峰值可大幅降低。

  • No labels