hudi索引

1.重点类

1.1.HoodieIndex

  索引实现的基类,核心方法是两个:tagLocation和updateLocation
  后续有不同的子类实现具体的索引
在这里插入图片描述

1.2.HoodieIndexFactory

  没有具体这个类,是创建HoodieIndex的工厂类。具体操作类的名字以这个为后缀:FlinkHoodieIndexFactory、SparkHoodieIndexFactory、JavaHoodieIndexFactory
  以FlinkHoodieIndexFactory来说,支持如下索引的创建

// TODO more indexes to be added
switch (config.getIndexType()) {
  case INMEMORY:
    return new FlinkInMemoryStateIndex(context, config);
  case BLOOM:
    return new HoodieBloomIndex(config, ListBasedHoodieBloomIndexHelper.getInstance());
  case GLOBAL_BLOOM:
    return new HoodieGlobalBloomIndex(config, ListBasedHoodieBloomIndexHelper.getInstance());
  case SIMPLE:
    return new HoodieSimpleIndex(config, Option.empty());
  case GLOBAL_SIMPLE:
    return new HoodieGlobalSimpleIndex(config, Option.empty());
  case BUCKET:
    return new HoodieSimpleBucketIndex(config);
  default:
    throw new HoodieIndexException("Unsupported index type " + config.getIndexType());
}

1.3.BaseHoodieBloomIndexHelper

  执行过滤步骤的类,两个实现类ListBasedHoodieBloomIndexHelper和SparkHoodieBloomIndexHelpe

1.4.HoodieBaseBloomIndexCheckFunction

  BaseHoodieBloomIndexHelper进行操作的核心,computeNext是进行bloom过滤的核心方法,关键点在于keyLookupHandle.addKey(recordKey)的调用

1.5.HoodieKeyLookupHandle

  就是1.4中的keyLookupHandle的类名,方法中利用了bloomFilter过滤

public void addKey(String recordKey) {
  // check record key against bloom filter of current file & add to possible keys if needed
  if (bloomFilter.mightContain(recordKey)) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("Record key " + recordKey + " matches bloom filter in  " + partitionPathFileIDPair);
    }
    candidateRecordKeys.add(recordKey);
  }
  totalKeysChecked++;
}

  bloomFilter的获取也在这个类当中

public HoodieKeyLookupHandle(HoodieWriteConfig config, HoodieTable<T, I, K, O> hoodieTable,
                             Pair<String, String> partitionPathFileIDPair) {
  super(config, hoodieTable, partitionPathFileIDPair);
  this.candidateRecordKeys = new ArrayList<>();
  this.totalKeysChecked = 0;
  this.bloomFilter = getBloomFilter();
}

1.6.HoodieFileReader

  文件读取类,有一个重点方法就是读取bloom过滤器readBloomFilter()
  有三个实现:HoodieParquetReader、HoodieOrcReader、HoodieHFileReader

1.7.BaseWriteHelper

  触发调用HoodieIndex的tagLocation接口的类,调用接口是tag(),tag接口由自身的write接口调用
  对Flink有点特殊,Flink重载了write接口,内部没有调用tag()。索引Flink流程可能没有使用bloom索引

2.流程

2.1.创建

  进行查询时相关类的构建的调用链

HoodieTable.getIndex -> HoodieIndexFactory.createIndex -> HoodieIndex

  其中,创建HoodieIndex时,参数中包含了BaseHoodieBloomIndexHelper
  相关配置项为hoodie.index.type

2.2.获取bloomFilter

  获取bloomFilter的整体调用流程为

BaseWriteHelper.write -> tag -> HoodieIndex.tagLocation -> lookupIndex -> BaseHoodieBloomIndexHelper.findMatchingFilesForRecordKeys -> HoodieBaseBloomIndexCheckFunction.apply -> LazyKeyCheckIterator.computeNext -> HoodieKeyLookupHandle.getBloomFilter -> HoodieFileReader.readBloomFilter -> BaseFileUtils.readBloomFilterFromMetadata

  获取bloomFilter以后,在HoodieKeyLookupHandle.addKey中使用

2.3.bloomFilter写入文件

  写入数据时产生将Key写入bloomFilter

DataWriter.write -> BulkInsertDataInternalWriterHelper.write -> HoodieRowCreateHandle.writeRow -> HoodieInternalRowParquetWriter.writeRow -> HoodieRowParquetWriteSupport.add -> HoodieBloomFilterWriteSupport.addKey -> BloomFilter.add

  之后将bloomFilter写入文件,因为是写入Parquet的Footer,好像是直接基于Parquet提供的接口,并没有直接调用写文件的接口

HoodieRowParquetWriteSupport.finalizeWrite -> HoodieBloomFilterWriteSupport.finalizeMetadata -> BloomFilter.serializeToString
public Map<String, String> finalizeMetadata() {
  HashMap<String, String> extraMetadata = new HashMap<>();

  extraMetadata.put(HOODIE_AVRO_BLOOM_FILTER_METADATA_KEY, bloomFilter.serializeToString());
  if (bloomFilter.getBloomFilterTypeCode().name().contains(HoodieDynamicBoundedBloomFilter.TYPE_CODE_PREFIX)) {
    extraMetadata.put(HOODIE_BLOOM_FILTER_TYPE_CODE, bloomFilter.getBloomFilterTypeCode().name());
  }

  if (minRecordKey != null && maxRecordKey != null) {
    extraMetadata.put(HOODIE_MIN_RECORD_KEY_FOOTER, minRecordKey.toString());
    extraMetadata.put(HOODIE_MAX_RECORD_KEY_FOOTER, maxRecordKey.toString());
  }

  return extraMetadata;
}

  Parquet最终写基于ParquetFileWriter

FinalizedWriteContext finalWriteContext = writeSupport.finalizeWrite();
Map<String, String> finalMetadata = new HashMap<String, String>(extraMetaData);
String modelName = writeSupport.getName();
if (modelName != null) {
  finalMetadata.put(ParquetWriter.OBJECT_MODEL_NAME_PROP, modelName);
}
finalMetadata.putAll(finalWriteContext.getExtraMetaData());
parquetFileWriter.end(finalMetadata);

2.4.索引的key

  索引配置是一个全局配置,没有针对某列建索引,Key的来源有很多种:可能是uuid、partition、primaryKey(filed.get(0)获取)、还有基于计算引擎数据类型获取的,具体看HoodieKey的构造
  此外有一个配置,设置key取哪个列:hoodie.datasource.write.recordkey.field,默认uuid

3.Parquet writer类结构

3.1.创建

  以HoodieRowCreateHandle起点看,在构造函数中,创建了HoodieInternalRowFileWriter

this.fileWriter = HoodieInternalRowFileWriterFactory.getInternalRowFileWriter(path, table, writeConfig, structType);

  这里会创建WriteSupport,然后作为HoodieInternalRowParquetWriter的成员

HoodieRowParquetWriteSupport writeSupport =
        new HoodieRowParquetWriteSupport(table.getHadoopConf(), structType, bloomFilterOpt, writeConfig);

return new HoodieInternalRowParquetWriter(
    path,
    new HoodieParquetConfig<>(
        writeSupport,
        writeConfig.getParquetCompressionCodec(),
        writeConfig.getParquetBlockSize(),
        writeConfig.getParquetPageSize(),
        writeConfig.getParquetMaxFileSize(),
        writeSupport.getHadoopConf(),
        writeConfig.getParquetCompressionRatio(),
        writeConfig.parquetDictionaryEnabled()
    ));

  HoodieInternalRowParquetWriter的构造函数里可以看到作为了独立的成员

public HoodieInternalRowParquetWriter(Path file, HoodieParquetConfig<HoodieRowParquetWriteSupport> parquetConfig)
    throws IOException {
  super(file, parquetConfig);

  this.writeSupport = parquetConfig.getWriteSupport();
}

  HoodieInternalRowParquetWriter最终的父类就是第三方的ParquetWriter,ParquetWriter的构造函数的参数包含WriteSupport

3.2.写数据

  写的时候,上层调用的是HoodieInternalRowParquetWriter,接口是writeRow,其中有对WriteSupport写接口的调用

public void writeRow(UTF8String key, InternalRow row) throws IOException {
  super.write(row);
  writeSupport.add(key);
}

  writer最终就是调用父类ParquetWriter的write接口
  writeSupport.add(key)是WriteSupport的接口,这里是做一个内存缓存

public void add(UTF8String recordKey) {
  this.bloomFilterWriteSupportOpt.ifPresent(bloomFilterWriteSupport ->
      bloomFilterWriteSupport.addKey(recordKey));
}

  缓存在finalizeWrite接口中使用

public WriteSupport.FinalizedWriteContext finalizeWrite() {
  Map<String, String> extraMetadata =
      bloomFilterWriteSupportOpt.map(HoodieBloomFilterWriteSupport::finalizeMetadata)
          .orElse(Collections.emptyMap());

  return new WriteSupport.FinalizedWriteContext(extraMetadata);
}

  finalizeWrite接口在第三方的InternalParquetRecordWriter的close方法调用
  HoodieBloomFilterWriteSupport::finalizeMetadata这一步是实现了写入自定义的元数据

public Map<String, String> finalizeMetadata() {
  HashMap<String, String> extraMetadata = new HashMap<>();

  extraMetadata.put(HOODIE_AVRO_BLOOM_FILTER_METADATA_KEY, bloomFilter.serializeToString());
  if (bloomFilter.getBloomFilterTypeCode().name().contains(HoodieDynamicBoundedBloomFilter.TYPE_CODE_PREFIX)) {
    extraMetadata.put(HOODIE_BLOOM_FILTER_TYPE_CODE, bloomFilter.getBloomFilterTypeCode().name());
  }

  if (minRecordKey != null && maxRecordKey != null) {
    extraMetadata.put(HOODIE_MIN_RECORD_KEY_FOOTER, minRecordKey.toString());
    extraMetadata.put(HOODIE_MAX_RECORD_KEY_FOOTER, maxRecordKey.toString());
  }

  return extraMetadata;
}

4.触发索引

4.1.建表语句

create table indexTest (
  id int,
  name string,
  price double
) using hudi
 location '/spark/hudi/'
 tblproperties (
  primaryKey ='id',
  type = 'mor',
  hoodie.index.type = 'BLOOM',
  hoodie.compact.inline = 'true'
 );

  HoodieIndexConfig控制配置项,Spark默认使用bloomFilter

private String getDefaultIndexType(EngineType engineType) {
  switch (engineType) {
    case SPARK:
      return HoodieIndex.IndexType.SIMPLE.name();
    case FLINK:
    case JAVA:
      return HoodieIndex.IndexType.INMEMORY.name();
    default:
      throw new HoodieNotSupportedException("Unsupported engine " + engineType);
  }
}

5.索引更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/420923.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ESU毅速丨不锈钢材料为什么在金属3D打印中的广泛应用

不锈钢是一种传统且常见的材料&#xff0c;在金属3D打印领域应用最广。那么&#xff0c;为何不锈钢材料在3D打印中如此受欢迎呢&#xff1f;以下是几个关键原因。 卓越的工艺适应性 金属3D打印技术&#xff0c;如直接金属激光烧结&#xff08;DMLS&#xff09;和选择性激光熔融…

【论文笔记】Improving Language Understanding by Generative Pre-Training

Improving Language Understanding by Generative Pre-Training 文章目录 Improving Language Understanding by Generative Pre-TrainingAbstract1 Introduction2 Related WorkSemi-supervised learning for NLPUnsupervised pre-trainingAuxiliary training objectives 3 Fra…

【MySQL】数据查询——DQL基本数据库查询

目录 查询语法1. 查询表中所有的数据行和列&#xff0c;采用“*”符号2. 查询表中指定列的数据。3. 在查询中使用别名&#xff0c;使用“AS”关键字。4. 在查询中使用常量列&#xff1a;如果需要将一些常量的默认信息添加到输出结果中&#xff0c;以方便统计或计算。可以使用常…

Linux服务器挂了后如何再次启动SVN

Linux服务器挂了后如何再次启动SVN 启动SVN步骤grep查询kill杀掉原有的select查找目录&#xff0c;并设置启动服务DONE设置自启动 启动SVN步骤 最近在折腾AI&#xff0c;比较少更博客了&#xff0c;大家有问题可随时询问 grep查询 [rootDujinyang Code]# ps -ef |grep svn …

【Qt学习】QTextEdit 与 QComboBox 的 属性与实例(槽函数的使用、读取本机内容到控件)

文章目录 1. QTextEdit2.1 介绍2.2 实例使用 - 槽函数的使用 2. QComboBox2.1 介绍2.2 实例使用案例1&#xff1a;设置下拉框项目组件的方式案例2&#xff1a;读取本机文件内容 到QComboBox 1. QTextEdit 2.1 介绍 我们可以查阅官方文档&#xff0c;对QTextEdit 有更深的了解&…

vue3基础教程(1)——nodejs环境搭建

博主个人小程序已经上线&#xff1a;【中二少年工具箱】 小程序二维如下&#xff1a; 正文开始 专栏简介1. 环境菜单2.为什么下载node3. nodejs简介4. nodejs安装5. 编辑器选择 专栏简介 本系列文章由浅入深&#xff0c;从基础知识到实战开发&#xff0c;非常适合入门同学。…

13.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-如果没有工具就创造工具

内容参考于&#xff1a; 易道云信息技术研究院VIP课 上一个内容 &#xff1a;12.游戏网络通信存在的问题 现在把游戏网络的架构看了一个小小的大概&#xff0c;可以用它的接口发数据接收数据了&#xff0c;如果真正想用它这一套东西&#xff0c;真正核心不在于它的接口而在于…

python实现AES加密解密

1. 前言 AES是一种对称加密&#xff0c;所谓对称加密就是加密与解密使用的秘钥是一个。 之前写过一片关于python AES加密解密的文章&#xff0c;但是这里面细节实在很多&#xff0c;这次我从 参数类型、加密模式、编码模式、补全模式、等等方面 系统的说明如何使用AES加密解密…

存储xss实现获取cookie(本地实战)

实战更能体验收获&#xff01;&#xff01;&#xff01; 环境准备&#xff1a; 1.phpstudy 2.dvwa靶场 实战 首先我们在phpstudy指定的localhost网站目录下编写一个xss.php文件&#xff0c;内容如下&#xff1a; <?php $cookie $_GET[cookie]; $ip getenv (REMOTE_…

零基础小白到底适不适合学鸿蒙,请看完这篇再决定吧~

随着华为鸿蒙系统的问世&#xff0c;不少技术小白在是否学习鸿蒙的问题上犹豫不决。鸿蒙作为华为自主研发的操作系统&#xff0c;拥有许多独特的技术优势和市场前景。但对于小白来说&#xff0c;是否值得投入时间和精力去学习鸿蒙开发呢&#xff1f; 1.鸿蒙系统开发&#xff1…

Java8 - LocalDateTime时间日期类使用详解

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

云南经贸Day01

day01 一. VMware创建虚拟机二.VMware安装Linux三 虚拟机网络配置1. 查看网络信息2. 修改网络IP 四. 虚拟机操作管理1. 通过VMware为虚拟机拍摄快照2. VMware 为虚拟机执行克隆 五. Xshell的安装和使用 一. VMware创建虚拟机 二.VMware安装Linux 清华大学镜像源网址: https://m…

南方电网的能源棋局上,蔚来换电扮演什么角色?

2 月 26 日&#xff0c;南网储能科技与蔚来能源签署协议&#xff0c;将充换电站、储能站、可调负载等聚合资源连接到虚拟电厂平台&#xff0c;推动换电站作为分布式储能在虚拟电厂项目上的应用。 蔚来换电站是国内首个智慧微电网型分布式换电设施&#xff0c;可透过换电订单预…

【C++ map和set】

文章目录 map和set序列式容器和关联式容器键值对setset的主要操作 mapmap主要操作 multiset和multimap map和set 序列式容器和关联式容器 之前我们接触的vector,list,deque等&#xff0c;这些容器统称为序列式容器&#xff0c;其底层为线性序列的的数据结构&#xff0c;里面存…

面试数据库篇(mysql)- 08事务

原理 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。 ACID是什么?可以详细说一下吗? 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全…

栈的概念结构和实现

文章目录 一、什么是栈二、栈的实现三、实现栈所需的函数四、完整栈的展现五、栈的思维导图 一、什么是栈 栈是一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除操作。进行插入和删除的一段叫做栈顶&#xff0c;另一段叫做栈底 压栈&#xff1a;插入数据 出栈&a…

ShardingSphere 5.x 系列【18】自定义类分片算法

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 3.1.0 本系列ShardingSphere 版本 5.4.0 源码地址:https://gitee.com/pearl-organization/study-sharding-sphere-demo 文章目录 1. 概述2. ClassBasedShardingAlgorithm3. 案例演示3.1 STANDARD3.2 COMPLEX…

【递归搜索回溯专栏】前言与本专栏介绍

本专栏内容为&#xff1a;递归&#xff0c;搜索与回溯算法专栏。 通过本专栏的深入学习&#xff0c;你可以了解并掌握算法。 &#x1f493;博主csdn个人主页&#xff1a;小小unicorn ⏩专栏分类&#xff1a;递归搜索回溯专栏 &#x1f69a;代码仓库&#xff1a;小小unicorn的代…

视频学习胜过读书吗

现在&#xff0c;网上的课程视频和讲座视频&#xff0c;越来越多。同样的内容&#xff0c;可以读书学习&#xff0c;也可以视频学习&#xff0c;大家喜欢哪一种&#xff1f; 我比较喜欢读书&#xff0c;实在没耐心视频学习。 书籍只要随手一翻&#xff0c;就知道大概的内容了&…

[Android View] 可绘制形状 (Shape Xml)

一切以官方文档为主 官方文档https://developer.android.com/guide/topics/resources/drawable-resource?hlzh-cn#Shape 什么是可绘制形状 可以理解为用xml文件来描述一个简单的Drawable图形&#xff0c;比如说以下这段xml就可以用来描述一个白色的圆形&#xff1a; <?…