最近随着项目的深入,发现hive meta有些弊端,就是你会发现它的元数据操作与操作物理集群的代码耦合在一起,非常不利于扩展。比如:在create_table的时候同时进行路径校验及创建,如下代码:

   if (!TableType.VIRTUAL_VIEW.toString().equals(tbl.getTableType())) {
if (tbl.getSd().getLocation() == null
|| tbl.getSd().getLocation().isEmpty()) {
tblPath = wh.getTablePath(
ms.getDatabase(tbl.getDbName()), tbl.getTableName());
} else {
if (!isExternal(tbl) && !MetaStoreUtils.isNonNativeTable(tbl)) {
LOG.warn("Location: " + tbl.getSd().getLocation()
+ " specified for non-external table:" + tbl.getTableName());
}
tblPath = wh.getDnsPath(new Path(tbl.getSd().getLocation()));
}
tbl.getSd().setLocation(tblPath.toString());
} if (tblPath != null) {
if (!wh.isDir(tblPath)) {
if (!wh.mkdirs(tblPath, true)) {
throw new MetaException(tblPath
+ " is not a directory or unable to create one");
}
madeDir = true;
}

   所以这是meta无法统一所有元数据的原因么。。其实hive metastore的代码从大的来看,就好比元数据的增删改查,从上次梳理中我们看到,在创建HiveMetaStore的init方法中,同时创建了三种Listener---MetaStorePreEventListener,MetaStoreEventListener,MetaStoreEndFunctionListener用于对每一步事件的监听与记录。同时呢,它还new出了WareHouse,用以进行物理操作。

  

     public void init() throws MetaException {
rawStoreClassName = hiveConf.getVar(HiveConf.ConfVars.METASTORE_RAW_STORE_IMPL);
initListeners = MetaStoreUtils.getMetaStoreListeners(
MetaStoreInitListener.class, hiveConf,
hiveConf.getVar(HiveConf.ConfVars.METASTORE_INIT_HOOKS));
for (MetaStoreInitListener singleInitListener: initListeners) {
MetaStoreInitContext context = new MetaStoreInitContext();
singleInitListener.onInit(context);
} String alterHandlerName = hiveConf.get("hive.metastore.alter.impl",
HiveAlterHandler.class.getName());
alterHandler = (AlterHandler) ReflectionUtils.newInstance(MetaStoreUtils.getClass(
alterHandlerName), hiveConf);
wh = new Warehouse(hiveConf);
。。。。
}

 接下来,我们从元数据的生命周期开始,学习下Partiiton的生命周期。在HiveMetaStoreClient中,查找add_partition作为入口,这种操作在我们insert overwrite 以表中某个字段为分区时,比如dt=20170830,作用到的操作。或者是add_partitions,创建分区表后进行数据的导入,那么会创建多个分区路径,下面以add_partiitons为例:

   public int add_partitions(List<Partition> new_parts)
throws InvalidObjectException, AlreadyExistsException, MetaException,
TException {
return client.add_partitions(new_parts);
} @Override
public List<Partition> add_partitions(
List<Partition> parts, boolean ifNotExists, boolean needResults)
throws InvalidObjectException, AlreadyExistsException, MetaException, TException {
if (parts.isEmpty()) {
return needResults ? new ArrayList<Partition>() : null;
}
Partition part = parts.get(0);
AddPartitionsRequest req = new AddPartitionsRequest(
16 part.getDbName(), part.getTableName(), parts, ifNotExists);
17 req.setNeedResult(needResults);
18 AddPartitionsResult result = client.add_partitions_req(req);
return needResults ? filterHook.filterPartitions(result.getPartitions()) : null;
}

  这里的client来自于ThriftHiveMetastore.Iface接口对象,其实现子类HiveMetaStore并调用init方法进行创建。随后将封装了AddPartitionsRequest类,其实这个类还是partition的属性,但是这样封装的好处是,今后再调用的时候不用再去获取partition的DbName,,TableName等信息,一次性封装以便后续直接使用该对象。随后,我们查看client调用add_partitions_req,下面代码高能预警,非常多,我们一点点分析。

  

    private List<Partition> add_partitions_core(
RawStore ms, String dbName, String tblName, List<Partition> parts, boolean ifNotExists)
throws MetaException, InvalidObjectException, AlreadyExistsException, TException {
logInfo("add_partitions");
boolean success = false;
// Ensures that the list doesn't have dups, and keeps track of directories we have created.
Map<PartValEqWrapper, Boolean> addedPartitions = new HashMap<PartValEqWrapper, Boolean>();
List<Partition> result = new ArrayList<Partition>();
List<Partition> existingParts = null;
Table tbl = null;
try {
ms.openTransaction();
tbl = ms.getTable(dbName, tblName);
if (tbl == null) {
throw new InvalidObjectException("Unable to add partitions because "
+ "database or table " + dbName + "." + tblName + " does not exist");
} if (!parts.isEmpty()) {
firePreEvent(new PreAddPartitionEvent(tbl, parts, this));
} for (Partition part : parts) {
if (!part.getTableName().equals(tblName) || !part.getDbName().equals(dbName)) {
throw new MetaException("Partition does not belong to target table "
+ dbName + "." + tblName + ": " + part);
}
boolean shouldAdd = startAddPartition(ms, part, ifNotExists);
if (!shouldAdd) {
if (existingParts == null) {
existingParts = new ArrayList<Partition>();
}
existingParts.add(part);
LOG.info("Not adding partition " + part + " as it already exists");
continue;
}
boolean madeDir = createLocationForAddedPartition(tbl, part);
if (addedPartitions.put(new PartValEqWrapper(part), madeDir) != null) {
// Technically, for ifNotExists case, we could insert one and discard the other
// because the first one now "exists", but it seems better to report the problem
// upstream as such a command doesn't make sense.
throw new MetaException("Duplicate partitions in the list: " + part);
}
initializeAddedPartition(tbl, part, madeDir);
result.add(part);
}
if (!result.isEmpty()) {
success = ms.addPartitions(dbName, tblName, result);
} else {
success = true;
}
success = success && ms.commitTransaction();
} finally {
if (!success) {
ms.rollbackTransaction();
for (Entry<PartValEqWrapper, Boolean> e : addedPartitions.entrySet()) {
if (e.getValue()) {
wh.deleteDir(new Path(e.getKey().partition.getSd().getLocation()), true);
// we just created this directory - it's not a case of pre-creation, so we nuke
}
}
fireMetaStoreAddPartitionEvent(tbl, parts, null, false);
} else {
fireMetaStoreAddPartitionEvent(tbl, result, null, true);
if (existingParts != null) {
// The request has succeeded but we failed to add these partitions.
fireMetaStoreAddPartitionEvent(tbl, existingParts, null, false);
}
}
}
return result;
}

  首先呢

  1、ms.openTransaction(),这个上次已经提到过,是为了保证操作的原子性。随后 tbl = ms.getTable(dbName, tblName);

  2、通过dbName以及tableName获取正个Table对象。

  3、通过firePreEvent记录事件。

  4、开始循环遍历partiiton,通过startAddPartition方法校验该partition是否在元数据中存在

  5、调用createLocationForAddedPartition方法进行文件路径创建,随后调用initializeAddedPartition,主要是将table的param信息赋给partition,与hive的表结构有关,最终会将param扩展信息写入类似meta_partition_param的扩展信息表。

  6、待物理操作完毕之后,进行ms.addPartitions(dbName, tblName, result)元数据信息的meta录入。

  7、如果说partition的路径已经存在,则抛出异常,并且在最后删除已经创建的路径。这个有一次,请看上面,首先创建了一个Map,

Map<PartValEqWrapper, Boolean> addedPartitions = new HashMap<PartValEqWrapper, Boolean>();将partition对象作为key,mkdir成功失败的布尔值作为value,最终通过判断value的值,来删除创建成功的partition.

  删除,和查询就不说了,因为太过简单,那么alter_partition来了,client.alter_partition(dbName, tblName, newPart);从client端调用我也不说了~,传入dbName,tbleName以及新的partition,随之在hivemetaStore中调用了rename_partition方法:

  

    @Override
public void rename_partition(final String db_name, final String tbl_name,
final List<String> part_vals, final Partition new_part)
throws InvalidOperationException, MetaException, TException {
// Call rename_partition without an environment context.
rename_partition(db_name, tbl_name, part_vals, new_part, null);
} private void rename_partition(final String db_name, final String tbl_name,
final List<String> part_vals, final Partition new_part,
final EnvironmentContext envContext)
throws InvalidOperationException, MetaException,
TException {
startTableFunction("alter_partition", db_name, tbl_name); if (LOG.isInfoEnabled()) {
LOG.info("New partition values:" + new_part.getValues());
if (part_vals != null && part_vals.size() > 0) {
LOG.info("Old Partition values:" + part_vals);
}
} Partition oldPart = null;
Exception ex = null;
try {
firePreEvent(new PreAlterPartitionEvent(db_name, tbl_name, part_vals, new_part, this)); if (part_vals != null && !part_vals.isEmpty()) {
MetaStoreUtils.validatePartitionNameCharacters(new_part.getValues(),
partitionValidationPattern);
} oldPart = alterHandler.alterPartition(getMS(), wh, db_name, tbl_name, part_vals, new_part); // Only fetch the table if we actually have a listener
Table table = null;
for (MetaStoreEventListener listener : listeners) {
if (table == null) {
table = getMS().getTable(db_name, tbl_name);
}
AlterPartitionEvent alterPartitionEvent =
new AlterPartitionEvent(oldPart, new_part, table, true, this);
alterPartitionEvent.setEnvironmentContext(envContext);
listener.onAlterPartition(alterPartitionEvent);
}
} catch (InvalidObjectException e) {
ex = e;
throw new InvalidOperationException(e.getMessage());
} catch (AlreadyExistsException e) {
ex = e;
throw new InvalidOperationException(e.getMessage());
} catch (Exception e) {
ex = e;
if (e instanceof MetaException) {
throw (MetaException) e;
} else if (e instanceof InvalidOperationException) {
throw (InvalidOperationException) e;
} else if (e instanceof TException) {
throw (TException) e;
} else {
throw newMetaException(e);
}
} finally {
endFunction("alter_partition", oldPart != null, ex, tbl_name);
}
return;
}

  我们继续来看:  

  1、startTableFunction方法主要用来计数

  2、new_part.getValues()其实获取的是partition的具体列值信息,比如dt=20170830,那么获取的就是这个20170830

  3、随之通过validatePartitionNameCharacters校验partitionName是否合法。

  4、随后通过alterHandler.alterPartition进行partition的更改,但是为什么要用oldPart命名?已经修改了呀?(疑问)我们跟进去会发现,其调用了updatePartColumnStats方法:

  private void updatePartColumnStats(RawStore msdb, String dbName, String tableName,
List<String> partVals, Partition newPart) throws MetaException, InvalidObjectException {
dbName = HiveStringUtils.normalizeIdentifier(dbName);
tableName = HiveStringUtils.normalizeIdentifier(tableName);
String newDbName = HiveStringUtils.normalizeIdentifier(newPart.getDbName());
String newTableName = HiveStringUtils.normalizeIdentifier(newPart.getTableName()); Table oldTable = msdb.getTable(dbName, tableName);
if (oldTable == null) {
return;
} try {
String oldPartName = Warehouse.makePartName(oldTable.getPartitionKeys(), partVals);
String newPartName = Warehouse.makePartName(oldTable.getPartitionKeys(), newPart.getValues());
if (!dbName.equals(newDbName) || !tableName.equals(newTableName)
|| !oldPartName.equals(newPartName)) {
msdb.deletePartitionColumnStatistics(dbName, tableName, oldPartName, partVals, null);
} else {
Partition oldPartition = msdb.getPartition(dbName, tableName, partVals);
if (oldPartition == null) {
return;
}
if (oldPartition.getSd() != null && newPart.getSd() != null) {
List<FieldSchema> oldCols = oldPartition.getSd().getCols();
if (!MetaStoreUtils.areSameColumns(oldCols, newPart.getSd().getCols())) {
updatePartColumnStatsForAlterColumns(msdb, oldPartition, oldPartName, partVals, oldCols, newPart);
}
}
}
} catch (NoSuchObjectException nsoe) {
LOG.debug("Could not find db entry." + nsoe);
//ignore
} catch (InvalidInputException iie) {
throw new InvalidObjectException("Invalid input to update partition column stats." + iie);
}
}

  5、通过Warehouse.makePartName组装partition的原有和新的表达,比如:dt=20180830,新的为dataPart=20180830

  6、这里会有层判断,如果新的表达与旧的表达不同则删除原有meta信息,否则将会调用updatePartColumnStatsForAlterColumns进行meta元数据的更新。

  随后就木有了。。太晚了,碎觉啦,明天还要作死上班呢哈哈哈哈~

Hive metastore源码阅读(二)的更多相关文章

  1. Hive metastore源码阅读(三)

    上次写了hive metastore的partition的生命周期,但是简略概括了下alter_partition的操作,这里补一下alter_partition,因为随着项目的深入,发现它涉及的地方 ...

  2. Hive metastore源码阅读(一)

    不要问我为什么,因为爱,哈哈哈哈...进入正题,最近做项目顺带学习了下hive metastore的源码,进行下知识总结. hive metastore的整体架构如图: 一.组成结构: 如图我们可以看 ...

  3. Hive cli源码阅读和梳理

    对Cli的重新认识*). hive cli有两种模式, 本地模式: 采用持有的driver对象来处理, 远程模式: 通过连接HiveServer来实现, 由此可见之前的架构图中的描述还是模糊且带有误导 ...

  4. xxl-job源码阅读二(服务端)

    1.源码入口 xxl-job-admin是一个简单的springboot工程,简单翻看源码,可以很快发现XxlJobAdminConfig入口. @Override public void after ...

  5. Spring 源码阅读 二

    程序入口: 接着上一篇博客中看完了在AnnotationConfigApplicationContext的构造函数中的register(annotatedClasses);将我们传递进来的主配置类添加 ...

  6. SparkConf加载与SparkContext创建(源码阅读二)

    紧接着昨天,我们继续开搞了啊.. 1.下面,开始创建BroadcastManager,就是传说中的广播变量管理器.BroadcastManager用于将配置信息和序列化后的RDD.Job以及Shuff ...

  7. JDK源码阅读(二) AbstractList

    package java.util; public abstract class AbstractList<E> extends AbstractCollection<E> i ...

  8. Struts2源码阅读(一)_Struts2框架流程概述

    1. Struts2架构图  当外部的httpservletrequest到来时 ,初始到了servlet容器(所以虽然Servlet和Action是解耦合的,但是Action依旧能够通过httpse ...

  9. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

随机推荐

  1. 带您了解mysql CONCAT()函数

    CONCAT()函数是mysql中非常重要的函数,可以将多个字符串连接成一个字符串,下文对该函数作了详细的阐述,希望对您有所帮助. mysql CONCAT()函数用于将多个字符串连接成一个字符串,是 ...

  2. ip 百度地图 php

    已知一个IP $ipname=api_hits($DT_IP); -------------- //apifunction getAddressComponent($ak, $longitude, $ ...

  3. Web API (四) 特性路由(Attribute Route)

    特性路由 是Web API 2 中提出的一种新的类型的路由,正如其名称那样,它是通过特性(Attribute) 来定义路由的,相比之前的基于模式(Convertion Based)的路由,特性路由 能 ...

  4. ADO.NET复习总结(6)-断开式数据操作

    一.基础知识 主要类及成员(和数据库无关的)(1)类DataSet:数据集,对应着库,属性Tables表示所有的表(2)类DataTable:数据表,对应着表,属性Rows表示所有的行(3)类Data ...

  5. IOS UI 滚动视图 UIScrollView

    UIScrollView 常用属性 scrollView.maximumZoomScale= 2.0; //  缩放最大比例 scrollView.minimumZoomScale = 0.2;// ...

  6. 更改sql多条数据,更新替换字符串中固定的字符串

    需求产生的背景: 数据库里建库时插入了字典数据,可是这个字典数据,有一些是不准确的,所以就需要把一些固定的数据查出来替换掉. 问题解决逻辑: 大体逻辑是,首先把固定需要替换的字符串提取出来,赋值给变量 ...

  7. 频繁更换ip会影响SEO优化吗?

    网站更换ip会不影响SEO的效果,其实网站更换ip是正常的(但不能频繁更换),搜索引擎抓取是根据网站的域名进行的,不是根据ip来抓取你的网站.在短时间内更换IP对SEO的效果并没有很大的影响. 如果是 ...

  8. [转]如何使用PHP实现javascript的escape和unescape函数

    前端开发工程师都知道javascript有编码函数escape()和对应的解码函数unescape(),而php中只有个urlencode和urldecode,这个编码和解码函数对encodeURI和 ...

  9. python_如何判断字符串a以某个字符串开头或结尾?

    案例: 某文件系统目录下有一系列文件: 1.c 2.py 3.java 4.sh 5.cpp ...... 编写一个程序,给其中所有的.sh文件和.py文件加上可执行权限 如何解决这个问题? 1. 先 ...

  10. WebSphere--安全性

    WebSphere应用服务器具有很好的安全性支持.安全性简单地说就是确定谁可访问重要的系统资源,这些系统资源包括文件.目录.程序.连接和数据库.以独立模式运行WebSphere应用服务器比作为 Web ...