Carbondata源码阅读(1) - Carbondata Presto Connector

好久没有写博客了。
最近开始读Apache Carbondata的源码,从presto connector开始读。因为一方面对scala和Spark源码不熟,另一方面是正在做的研究课题是以presto为基础的。

其实carbondata对spark的支持比较好,在源码中有一万多行的scala代码是专门给spark写的一些元数据缓存和查询优化。carbondata本身是华为在主导,也是中国本土企业贡献的第一个apache顶级项目。carbondata的presto connector主要是美团开发的,美团据说用presto比较多。

我用的环境是ubuntu16.04 + openjdk8 + intellij idea 2017.1

https://github.com/apache/carbondata fork了一个repo到自己的github账号,把源码clone下来,用intellij直接打开就可以。carbondata是用maven管理的项目,intellij会自动识别、下载依赖、创建工程,只是下载依赖可能需要点时间。

carbondata的核心源码都在core目录下。hadoop目录下有hadoop inputformat和outputformat的实现。integration目录下是carbondata和其他查询引擎之间的集成模块,包括hive、spark、spark2、presto等。其中presto下就是presto connector的代码。


Presto Connector

presto connector是presto和其他系统连接的一套接口,参考https://prestodb.io/docs/current/connector.html
presto通过各种不同的Connector从不同的系统中获取元数据和数据。connector中比较主要的类是Plugin、ConnectorFactory、Connector。
编译好的connector实现是一个jar包,放在presto安装目录的plugin子目录下。presto通过扫描找到Plugin接口的实现,调用Plugin中的getConnectorFactories方法,获得ConnectorFactory的实例。ConnectorFactory中的create方法可以创建一个connector实例。

Carbondata的presto connector中这三个接口的实现分别为:CarbondataPlugin、CarbondataConnectorFactory和CarbondataConnector。

FileFactory

在CarbondataPlugin中的getClassLoader方法中,返回了一个FileFactory的classLoader。这个类会在之后被加载和初始化。FileFactory是core中的类,提供一些文件操作的静待方法,可以用来读写本地的或者是HDFS\Alluxio上的carbondata文件。

CarbondataConnector

CarbondataConnector非常简单。其中有几个主要的成员:

private final LifeCycleManager lifeCycleManager;
private final CarbondataMetadata metadata;
private final ConnectorSplitManager splitManager;
private final ConnectorRecordSetProvider recordSetProvider;
private final ClassLoader classLoader;
private final ConnectorPageSourceProvider pageSourceProvider;

其中CarbondataMetadata是carbondata中对presto connector的metadata接口的实现,和元数据相关的函数都在这里,例如getTableSchema,listTableColumns等。

CarbondataMetadata

CarbondataMetadata中一个很重要的成员是:

private CarbonTableReader carbonTableReader;

CarbondataMetadata中几乎所有的方法都用到了这个成员。而CarbonTableReader是对CarbonMetadata(core中用来读写carbondata元数据的单例,注意不是CarbondataMetadata)和FileFactory的封装。

另外,该类中的:
getTableMetadata方法用来获取一个table的元数据,这是一个private方法,被该类中的其他方法调用。其主要代码如下:

    CarbonTable carbonTable = carbonTableReader.getTable(schemaTableName);
    List<ColumnMetadata> columnsMetaList = new LinkedList<>();
    List<CarbonColumn> carbonColumns = carbonTable.getCreateOrderColumn(schemaTableName.getTableName());
    for (CarbonColumn col : carbonColumns) {
      //show columns command will return these data
      Type columnType = CarbondataType2SpiMapper(col.getColumnSchema());
      ColumnMetadata columnMeta = new ColumnMetadata(col.getColumnSchema().getColumnName(), columnType);
      columnsMetaList.add(columnMeta);
    }
    //carbondata connector's table metadata
    return new ConnectorTableMetadata(schemaTableName, columnsMetaList);

其中carbonTable是CarbonTable的实例,该类是core.metadata.schema.table下的类,又一个关键的类浮出水面。。。createOrderColumn方法获取的是一个表在建表时的column顺序。

getTableHandle可以获取一个table的CarbondataTableHandle实例,这个实例可以在getColumnHandles中作为参数。

getColumnHandles方法返回的是column name和ColumnHandle的Map。实际上这个返回的Map中放的是CarbondataColumnHandle(ColumnHandle接口的实现)的实例。该类中有与column相关元数据并提供了与column元数据操作相关的方法,其中getColumnMetadata方法可用来获取column的元数据对象,其实就是把CarbondataColumnHandle中的成员封装成ColumnMetadata。

CarbonTableReader

这个类算是carbondata presto connector中最关键的一个类了。这个类中不但如上所述封装了FileFactory和CarbonMetadata的一些操作,还有很多和carbondata core打交道的方法。可以从这个类开始阅读carbondata core的源码。
这个类中涉及到的比较关键的carbondata的类和接口包括:
CacheClient:这个名字起的有点让人懵逼,它主要就是有这么个成员:

CacheAccessClient<TableSegmentUniqueIdentifier, SegmentTaskIndexWrapper>
      segmentAccessClient;

这个成员就是维护了Segment到SegmentTaskIndexWrapper的映射。CacheAccessClient中封装了一个Cache成员,这个Cache就是个K-V映射,之所以叫Cache是因为它有淘汰机制,可以把Cache在内存中的东西持久化到磁盘。那么主要问题是SegmentTaskIndexWrapper是啥。这个名字同样起的不知所云,它里面主要就是有这么个成员:

private Map<SegmentTaskIndexStore.TaskBucketHolder, AbstractIndex> taskIdToTableSegmentMap;

AbstractIndex下面会讲,TaskBucketHolder同样让人懵逼,目前还不清楚Bucket和Task是个啥概念,和Spark、Hive中的Task和Bucket有啥关系,它里面就俩成员以及equals和hashCode方法:

    public String taskNo;
    public String bucketNumber;

大概瞄了瞄注释,感觉可能是一个segment在创建的时候是由多个task完成的,每个task创建了一些数据块,这些数据块是一个bucket。bucket可能是介于segment和block之间的一个东西,文档里没有看到这个。
如果以上猜测正确,那么CacheClient就是一个从segment到bucket,再由bucket到这个bucket的b tree 根节点的一个两层映射。后面在仔细读代码验证猜测。

AbstractIndex: 对DataRefNode进行了一次封装,加了写乱七八糟的属性和方法。

DataRefNode: CarbonData中b-tree索引的节点,一个HDFS数据块对应一个DataRefNode(待确定),CarbonData中一个segment下的数据块构成一棵b-tree(也可能是多棵?每个bucket一课b tree?),同一个segment中的数据是有序的(文档里说的,但是如果有bucket的存在,不知道要怎么保证segment内的数据全局有序),可以根据查询条件进行数据块的过滤。一个b tree的根节点也是一个DataRefNode。

CarbonTable,这个名字很清楚,就是一个carbon table。

CarbonFile,这个名字也很清楚,就是一个carbon file,注意这个file可能是一个目录,它有listFiles方法可以列出子目录和文件。后缀面是mdt的carbon file貌似很神奇,因为源码里有这样一段:

    for (CarbonFile cf : carbonFileList.listFiles()) {
      if (!cf.getName().endsWith(".mdt")) {
        for (CarbonFile table : cf.listFiles()) {
          tableList.add(new SchemaTableName(cf.getName(), table.getName()));
        }
      }
    }

改天跑一下carbondata研究下mdt是啥。从源码看,一个carbondata的store path下,有一层目录是存各个 schema的,每个schema下有一层目录是存各个table的。

其他类

carbondata presto connector中的其他类就是这些了:
CarbondataModule:这个没啥,就是加载一些其他的类。

CarbondataSplit:主要的成员是:

private final CarbonLocalInputSplit localInputSplit;

记录了一个split(block)在HDFS(也可能是本地或者Alluxio)上的存储位置、所属的segment id等等的元信息。

CarbondataSplitManager:具有一个CarbonTableReader类型的成员,通过reader、根据查询的过滤条件获得相关的splits信息。

CarbondataRecordCursor:这是一个游标,负责读取split,split需要在构造时传入。其中有两个成员需要注意:

  private CarbonIterator<Object[]> rowCursor;
  private CarbonReadSupport<Object[]> readSupport;

CarbonIterator就是个普通的迭代器抽象类,加了个只会抛异常的默认remove方法。
CarbonReadSupport负责把读上来的数据组装成行。

CarbondataRecordSet:产生ConbondataRecordCursor的地方。

CarbondataRecordSetProvider:产生CarbondataRecordSet的地方,含有一个CarbonTableReader类型的成员。功能和CarbondataSplitManager类似,但是这里产生的是CarbondataRecordSet.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值