Hive Metastore动态切换存储引擎方案探索
Posted 咬定青松
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hive Metastore动态切换存储引擎方案探索相关的知识,希望对你有一定的参考价值。
Hadoop统一封装了对底层不同存储引擎的支持,且通过开放一致的API接口,便于调用方切换不同的存储访问,但是在Hadoop之上的Hive因为通过静态的配置方式来访问存储引擎,且对外不暴露接口,调用方切换引擎十分不变,本文来探讨下动态切换存储的可行性和方案。
Hadoop如何动态切换存储引擎
在Hadoop 的文件系统中能够根据路径和配置信息生成FileSystem,接口定义是:
public static FileSystem get(URI uri, Configuration conf);
其中FileSystem代表了通用抽象的文件系统基类,其实现可以是本地磁盘文件系统,也可以是分布式文件系统以及第三方对象存储系统。本地文件系统的实现类是LocalFileSystem,分布式文件系统的实现类是DistributedFileSystem、对象存储系统实现类比如S3AFileSystem、OBSFileSystem等。通过FileSystem,可以实现对底层存储引擎的读写操作。拿MinIO(一种兼容S3协议的对象存储系统)为例,创建其FileSystem:
Configuration conf = new Configuration();
conf.set("fs.s3a.impl", "org.apache.hadoop.fs.s3a.S3AFileSystem");
conf.set("fs.s3a.access.key", "admin123");
conf.set("fs.s3a.secret.key", "admin123");
conf.set("fs.s3a.endpoint", "http://ip-address-for-minio:9000");
String warehouse = "s3a://tmp/iceberg/warehouse";
FileSystem fileSystem=FileSystem.get(new URI(warehouse), conf);
S3AFileSystem s3AFileSystem= (S3AFileSystem) S3AFileSystem.get(new URI(warehouse), conf);
s3AFileSystem.delete(new Path(warehouse),true);
以上配置是创建S3AFileSystem文件系统,必需的几个配置属性。具体来讲,生成S3AFileSystem,有两个主要步骤:S3AFileSystem对象创建和客户端初始化。
S3AFileSystem对象创建
在FileSystem抽象类中根据具体文件系统Schema(hdfs、s3a、obs等)的实现类,通过反射自动生成FileSystem实例:
Class<? extends FileSystem> clazz =
getFileSystemClass(uri.getScheme(), conf);
Constructor<T> meth = clazz.getDeclaredConstructor(new Class[]);
meth.setAccessible(true);
result = meth.newInstance();
这里用到的是s3a 属性fs.s3a.impl指定的实现类:org.apache.hadoop.fs.s3a.S3AFileSystem。
客户端初始化
S3ClientFactory.S3ClientCreationParameters parameters = null;
parameters = new S3ClientFactory.S3ClientCreationParameters()
.withCredentialSet(credentials)
.withEndpoint(endpoint)
.withRequestHandlers(auditManager.createRequestHandlers());
//AmazonS3 s3
s3 = ReflectionUtils.newInstance(s3ClientFactoryClass, conf)
.createS3Client(getUri(),
parameters);
这里通过S3ClientFactory创建访问FileSystem的客户端,用到的主要属性是s3a 配置的endpoint和秘钥对。
从上述过程来看,动态切换不同的存储引擎,相当容易,只需要更改上述配置即可,比如切换到华为OBS,只需要配置形如下面的属性:
fs.obs.impl:org.apache.hadoop.fs.obs.OBSFileSystem
fs.AbstractFileSystem.obs.impl:org.apache.hadoop.fs.obs.OBS
fs.obs.access.key:xxx
fs.obs.secret.key:xxx
fs.obs.endpoint:obs.cn-southwest-2.myhuaweicloud.com
对于秘钥对的配置方式,AWS提供了很多种AWSCredentialsProvider接口的实现类,而且允许实现自定义获取方式:
public interface AWSCredentialsProvider
/**
* Returns AWSCredentials which the caller can use to authorize an AWS request.
* Each implementation of AWSCredentialsProvider can chose its own strategy for
* loading credentials. For example, an implementation might load credentials
* from an existing key management system, or load new credentials when
* credentials are rotated.
*
* @return AWSCredentials which the caller can use to authorize an AWS request.
*/
public AWSCredentials getCredentials();
/**
* Forces this credentials provider to refresh its credentials. For many
* implementations of credentials provider, this method may simply be a
* no-op, such as any credentials provider implementation that vends
* static/non-changing credentials. For other implementations that vend
* different credentials through out their lifetime, this method should
* force the credentials provider to refresh its credentials.
*/
public void refresh();
但是该接口只能动态获取秘钥对,而且秘钥对本身跟endpoint是无法关联的,使用AWSCredentialsProvider实现类的方式适合在endpoint确定的前提下使用。
Hive Metastore如何实现对存储引擎的访问
当FileSystem跟Hive Metastore结合时,相比Hadoop直接使用FileSystem,问题要复杂得多,具体表现为:
1. Hive Metastore是有关元数据操作的,也会涉及到FileSystem操作
Metastore作为rpc实现对外服务,在内部除了对元数据操作外,也会涉及到对底层存储数据的操作,这会调用FileSystem相关接口,比如:
当在HMS中创建Database时,会根据Database中指定的路径创建目录;
当在HMS中创建表时,会根据Table中指定的路径创建数据目录;
在drop database时,可能会删除Table的数据目录和文件;
在drop table时,可能会删除Table的数据目录和文件;
在truncate表时,会删除表数据文件;
在添加、删除分区时,会创建、删除数据分区目录;
在表重命名时,可能涉及到数据存储目录的变更;
2. 创建FileSystem所需要的属性无法通过rpc接口传递
以创建表和删除表的接口实现为例:
public class HiveMetaStore extends ThriftHiveMetastore
@Override
public void create_table_with_environment_context(final Table tbl)
create_table_core(getMS(), tbl, null);
@Override
public void drop_table(final String dbname, final String name, final boolean deleteData)
throws NoSuchObjectException, MetaException
drop_table_with_environment_context(dbname, name, deleteData, null);
其中Table各属性为:
private String tableName; // required
private String dbName; // required
private String owner; // required
private int createTime; // required
private int lastAccessTime; // required
private int retention; // required
private StorageDescriptor sd; // required
private List<FieldSchema> partitionKeys; // required
private Map<String,String> parameters; // required
private String viewOriginalText; // required
private String viewExpandedText; // required
private String tableType; // required
private PrincipalPrivilegeSet privileges; // optional
private boolean temporary; // optional
private boolean rewriteEnabled; // optional
private CreationMetadata creationMetadata; // optional
private String catName; // optional
private PrincipalType ownerType; // optional
可见,虽然创建表和删除表都需要访问FileSystem,但是我们却无法从该方法中传递其需要的属性。唯一有可能传递参数的数据结构是:
private Map<String,String> parameters;
但是FileSystem并不会从这里获取,FileSystem所需要的属性从哪里来呢?
3. 创建FileSystem所需要的属性只能静态加载
HMSHandler承接了所有rpc接口的功能实现,其所需要的外部配置属性都来源于Configuration,而Configuration则在HiveMetaStore启动时候从环境变量中加载。
public class HiveMetaStore extends ThriftHiveMetastore
...
public static void main(String[] args) throws Throwable
final Configuration conf = MetastoreConf.newMetastoreConf();
HiveMetastoreCli cli = new HiveMetastoreCli(conf);
cli.parse(args);
Properties hiveconf = cli.addHiveconfToSystemProperties();
...
startMetaStore(cli.getPort(), HadoopThriftAuthBridge.getBridge(), conf, startLock,
startCondition, startedServing)
...
public static void startMetaStore(int port, HadoopThriftAuthBridge bridge,
Configuration conf, Lock startLock, Condition startCondition,
AtomicBoolean startedServing) throws Throwable
...
HMSHandler baseHandler = new HiveMetaStore.HMSHandler("new db based metaserver", conf,
false);
IHMSHandler handler = newRetryingHMSHandler(baseHandler, conf);
...
上述代码表明了HiveMetaStore启动时从环境变量中查找所需的配置文件,然后启动HMSHandler rpc服务,HiveMetaStore查找的配置文件依次来源于:
hive-default.xml hive-site.xml hivemetastore-site.xml metastore-site.xml
除了通过配置文件,还可以通过conf 环境变量传给HiveMetaStore,但是一旦HiveMetaStore启动起来,这些属性也就确定了,意味着无法动态修改FileSystem所需要的配置信息。
通过配置文件配置参数的方式也是有局限性的,比如:
<property>
<name>fs.obs.impl</name>
<value>org.apache.hadoop.fs.obs.OBSFileSystem</value>
</property>
<property>
<name>fs.AbstractFileSystem.obs.impl</name>
<value>org.apache.hadoop.fs.obs.OBS</value>
</property>
<property>
<name>fs.obs.access.key</name>
<value>xxx</value>
<description>huaweicloud access key</description>
</property>
<property>
<name>fs.obs.secret.key</name>
<value>xxx</value>
<description>huaweicloud secret key</description>
</property>
<property>
<name>fs.obs.endpoint</name>
<value>xxx</value>
<description>huaweicloud endpoint</description>
</property>
因为属性名称是唯一的,同一种存储引擎,只能有一个实例,比如不能配置多个s3a存储引擎。
Hive Metastore实现动态切换存储引擎的方案
通过静态配置的方式肯定不行,那只能从接口传递参数入手,且只能从Map类型的参数入手最简单,比如下面示例中,给定Map属性和Table,将其设置到Table的表参数:
//Table tbl
Map<String, String> parameters = Optional.ofNullable(tbl.getParameters())
.orElseGet(HashMap::new);
//Map<String, String> tableProps
tableProps.forEach((key, value) ->
parameters.put(key, value);
);
tbl.setParameters(parameters);
//HiveMetaStoreClient metaClients
metaClients.createTable(tbl);
这里是从API层面来看的,下面从SQL层,比如Flink SQL,如果能够将秘钥对以及endpoint属性通过with语句来创建Catalog、database以及Table,那么事情就完成了一半:
CREATE CATALOG hive_catalog WITH (
'fs.obs.access.key' = 'xxx',
'fs.obs.secret.key' = 'xxx',
'fs.obs.endpoint' = 'huaweicloud endpoint'
'uri'='thrift://localhost:9083',
'warehouse'='s3a://tmp/warehouse/path'
);
CREATE DATABASE [IF NOT EXISTS] [catalog_name.]db_name
[COMMENT database_comment]
WITH (key1=val1, key2=val2, ...)
CREATE TABLE Orders (
user BIGINT,
product STRING,
order_time TIMESTAMP(3)
) WITH (
'fs.obs.access.key' = 'xxx',
'fs.obs.secret.key' = 'xxx',
'fs.obs.endpoint' = 'huaweicloud endpoint'
);
事情的另一半是在hms 接口实现端取出上述参数,传递到Configuration,然后再动态构建FileSystem及其 client。
小结
Hadoop在FileSystem API层面统一了底层存储引擎,用户可以轻易切换存储,而Hive Metastore 在Hadoop 之上,对外屏蔽了存储访问接口,通过配置文件的方式暴露交互接口,但是配置文件的方式是静态的,且对同一类型的文件系统,只能配置一个,在大数据场景下,该交互方式很受限制,本文探讨了HMS多存储引擎访问的实现方式,然后基于HMS本身如何解决动态切换存储的方案。
以上是关于Hive Metastore动态切换存储引擎方案探索的主要内容,如果未能解决你的问题,请参考以下文章