在Impala中,invalidate metadata与refresh语句都可以用来刷新表,但它们本质上还是不同的。
我们一般会采用传统的MySQL或PostgreSQL数据库作为Hive的Metastore(元数据存储)组件。在CDH中默认是MySQL,我们可以通过show tables in hive语句清晰地看到Hive Metastore中的各个表。
mysql> show tables in hive;
+---------------------------+
| Tables_in_hive |
+---------------------------+
| BUCKETING_COLS |
| CDS |
| COLUMNS_V2 |
| COMPACTION_QUEUE |
| COMPLETED_TXN_COMPONENTS |
| DATABASE_PARAMS |
| DBS |
| DB_PRIVS |
| DELEGATION_TOKENS |
| FUNCS |
| FUNC_RU |
| GLOBAL_PRIVS |
| HIVE_LOCKS |
| IDXS |
| INDEX_PARAMS |
| MASTER_KEYS |
| METASTORE_DB_PROPERTIES |
| NEXT_COMPACTION_QUEUE_ID |
| NEXT_LOCK_ID |
| NEXT_TXN_ID |
| NOTIFICATION_LOG |
| NOTIFICATION_SEQUENCE |
| NUCLEUS_TABLES |
| PARTITIONS |
| PARTITION_EVENTS |
| PARTITION_KEYS |
| PARTITION_KEY_VALS |
| PARTITION_PARAMS |
| PART_COL_PRIVS |
| PART_COL_STATS |
| PART_PRIVS |
| ROLES |
| ROLE_MAP |
| SDS |
| SD_PARAMS |
| SEQUENCE_TABLE |
| SERDES |
| SERDE_PARAMS |
| SKEWED_COL_NAMES |
| SKEWED_COL_VALUE_LOC_MAP |
| SKEWED_STRING_LIST |
| SKEWED_STRING_LIST_VALUES |
| SKEWED_VALUES |
| SORT_COLS |
| TABLE_PARAMS |
| TAB_COL_STATS |
| TBLS |
| TBL_COL_PRIVS |
| TBL_PRIVS |
| TXNS |
| TXN_COMPONENTS |
| TYPES |
| TYPE_FIELDS |
| VERSION |
+---------------------------+
54 rows in set (0.00 sec)
它的组织方式与MySQL中的information_schema类似。如TBLS保存有所有表的元数据,COLUMNS保存有所有列的元数据,PARTITIONS存储分区信息,SDS存储表及分区对应的HDFS目录映射,等等。
Impala作为一个MPP查询引擎,经常会配合Hive一同使用,我们的业务中也是如此。下图示出Impala及周边组件的大体结构。
Impala的核心组件是impalad,它负责提供所有查询服务。另外,还有catalogd负责获取与缓存表元数据,statestored则负责表元数据到每个impalad的更新。
这种方案完美解决了每次查询都要获取表元数据的问题,因为一旦表结构非常复杂或者数据很多,获取元数据会造成很大的延迟。如果将它们缓存下来,元数据就可以重用,节省时间。
但是,它又带来了一个新的问题:由于缓存不会即时刷新,当在Hive本身进行元数据甚至数据的更改时,Impala无法感知到。常见的情境如在Hive中新建了一张表,或者直接在Hive表对应的HDFS目录中新增文件等。所以,Impala才提供了invalidate metadata与refresh两条语句来打补丁。
invalidate的意思是“使无效、使作废”,因此invalidate metadata的含义就是“废除(缓存的)元数据”。它的语法是:
invalidate metadata; -- 废除所有表的元数据
invalidate metadata [table]; -- 废除表table的元数据
(1)I获取到表table,对catalogd发起resetMetadata请求;
(2)catalogd收到该请求,执行invalidateTable操作,清除所有与table相关的元数据缓存,重新读取Metastore中的所有元数据,并生成新的缓存。但是此时生成的缓存只包含库名和表名,是不完整的;
(3)catalogd再生成一个标记缓存的版本号,将这个不完整的缓存和版本号一起返回给I,然后继续异步加载其余的元数据;
(4)I收到catalogd返回的不完整缓存和版本号,用它来更新本地缓存。
invalidate metadata的特点就是异步性和全量性。从上面可以看出,在刚执行完时,除了I之外的其他impalad仍然保有旧的元数据缓存,就算I保有的新元数据也是残缺的。只有当catalogd异步加载完了table对应的所有元数据,才会生成一个更新的版本号,并将完整的元数据通过statestored广播给所有impalad,整个Impala集群的元数据感知才会达到一致。
refresh的意思比较简单,“刷新”。它的语法是:
refresh [table]; -- 刷新表table的元数据
refresh [table] partition [partition]; -- 刷新表table的partition分区元数据
在I上执行refresh table语句会发生如下的动作:
(1)I获取到表table,对catalogd发起resetMetadata请求;
(2)catalogd收到该请求:对指定了partition的请求,执行reloadPartition操作,获取该分区最新的元数据并刷新;对未指定partition的请求,执行reloadTable操作,获取全部分区最新的元数据并刷新。这里的“刷新”是指Metastore中与缓存对比如果没有变化,就保持原状;如果有增删改,才会发生改变;
(3)I收到catalogd返回的完整缓存,用它来更新本地缓存。
当然,statestored仍会负责广播新的元数据到其他节点。在广播完之前,除了I之外的其他impalad也保有旧的缓存。
由此可见,与invalidate metadata不同,refresh的特点是同步性和增量性。并且,它的执行是围绕单表以及单表的分区进行的,因此它更轻量级,也更适合分区元数据或数据文件更改之后的刷新。
通过上面的简单分析,容易做出以下总结:
1.如果数仓中发生了增删表或改变表结构的行为,如create table
、drop table
、alter table add column
等,就使用invalidate metadata [table]
语句。
2.如果数仓中某表加入了新数据,或者有分区的改动,如load data
、alter table add partition
等,就使用refresh [table] (partition [partition])
语句。
3.invalidate metadata比起refresh而言要重量级得多,并且它造成impalad之间查询不一致也会更严重。因此,也几乎禁止使用不带表名的invalidate metadata语句。
4.如果数仓中涉及到非常大批量的元数据更改,那么建议直接重启catalogd和statestored,这总比使用不带表名的invalidate metadata来得高效一些。