다음 내용은 charsyam 님께서 블로그에 올려주신 내용을 토대로하여 하였습니다. (https://charsyam.wordpress.com/2019/04/26/%EC%9E%85-%EA%B0%9C%EB%B0%9C-hive-metastore-%EC%97%90%EC%84%9C-location%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B4%80%EB%A6%AC%EB%90%A0%EA%B9%8C/?fbclid=IwAR12jAsZA4CxAKD6dVulsj9WCxdclcr4sY38DcfZ0tU2xXNroSwklyuHZR4)
CREATE TABLE `test1`(
`id` bigint
PARTITIONED BY (
`datestamp` date)
ROW FORMAT SERDE
'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
STORED AS INPUTFORMAT
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat'
LOCATION
'hdfs://a.b.c:8020/user/hive/warehouse/charsyam.db/test1'
TBLPROPERTIES (
'transient_lastDdlTime'='1556186715')
위와 같이 생성된 test1 이라는 table을 시간이 지난 후 drop table하고자 했는데 다음과 같은 에러가 발생하였다.
FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. MetaException(message:java.lang.IllegalArgumentException: Wrong FS: hdfs://a.b.c:8020/user/hive/warehouse/charsyam.db/test1, expected: hdfs://ip-b.b.c:8020)
이 에러는 hadoop에서 checkPath라는 함수에 의해서 발생한 에러로 Hive Metadata Table schema에 있는 Location 경로와 현재 hdfs에서 인식하는 자신의 hdfs 도메인이 일치하지 않을 때 발생하는 에러입니다.
즉 a.b.c도 ip가 1.1.1.1이고 b.b.c도 ip가 1.1.1.1 이지만 hdfs에서 도메인으로 바라볼 경우 hdfs://a.b.c:8020과 hdfs://b.b.c:8020은 엄연히 다른 도메인이 되는 것이다.
protected void checkPath(Path path) {
URI uri = path.toUri();
String thatScheme = uri.getScheme();
if (thatScheme == null) // fs is relative
return;
URI thisUri = getCanonicalUri();
String thisScheme = thisUri.getScheme();
//authority and scheme are not case sensitive
if (thisScheme.equalsIgnoreCase(thatScheme)) {// schemes match
String thisAuthority = thisUri.getAuthority();
String thatAuthority = uri.getAuthority();
if (thatAuthority == null && // path's authority is null
thisAuthority != null) { // fs has an authority
URI defaultUri = getDefaultUri(getConf());
if (thisScheme.equalsIgnoreCase(defaultUri.getScheme())) {
uri = defaultUri; // schemes match, so use this uri instead
} else {
uri = null; // can't determine auth of the path
}
}
if (uri != null) {
// canonicalize uri before comparing with this fs
uri = canonicalizeUri(uri);
thatAuthority = uri.getAuthority();
if (thisAuthority == thatAuthority || // authorities match
(thisAuthority != null &&
thisAuthority.equalsIgnoreCase(thatAuthority)))
return;
}
}
throw new IllegalArgumentException("Wrong FS: " + path +
", expected: " + this.getUri());
}
다음과 같은 일은 빈번히 발생하지는 않지만, hostname이 변경되었는데 변경된 hostname 정보로 Hive Metadata가 변경되지 않으면, 다음과 같은 에러가 발생할 수 있다.
생성된 테이블에 대한 Location이나 Format 정보들을 보고 싶다면 show create table '테이블명'; 을 쳐보면 된다.
그럼 이 정보들은 어디에 저장되는 것일까?
우선 Hive Metadata는 별도의 DBMS에 주로 저장을 합니다. 여기서는 MySQL이나 MariaDB에 저장한 것을 가정하여 설명하겠습니다.
MySQL에 접근해 보면 다음과 같이 Hive Metastore 데이터베이스를 확인 할 수 있습니다.
저의 경우 metastore라는 이름으로 데이터베이스를 생성하였습니다.
그리고 Hive Metastore에 만들어진 59개의 테이블들 중 DBS, TBLS, SDS만 살펴보면 됩니다.
마지막 S는 복수의 S로 보이며, 각각 Database, Table, StorageDescriptor 의 약자로 보입니다.
먼저 Hive에서 create database로 DB를 생성할 때 마다 해당 정보가 DBS에 저장됩니다.
DBS의 테이블 스키마는 다음과 같습니다.
CREATE TABLE `DBS` (
`DB_ID` bigint(20) NOT NULL,
`DESC` varchar(4000) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`DB_LOCATION_URI` varchar(4000) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`NAME` varchar(128) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`OWNER_NAME` varchar(128) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`OWNER_TYPE` varchar(10) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`CREATE_TIME` int(11) DEFAULT NULL,
PRIMARY KEY (`DB_ID`),
UNIQUE KEY `UNIQUE_DATABASE` (`NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
여기서 주목할 것은 DB_ID와 DB_LOCATION_URI 입니다. DB_LOCATION에 hdfs://a.b.c./user/hive/warehouse/default/ 까지 DB의 위치가 들어가게 됩니다. 그리고 이 DB에 테이블이 생성되면 table location은 항상 hdfs://a.b.c./user/hive/warehouse/default/ 하위로 생기게 됩니다.
CREATE TABLE `TBLS` (
`TBL_ID` bigint(20) NOT NULL,
`CREATE_TIME` int(11) NOT NULL,
`DB_ID` bigint(20) DEFAULT NULL,
`LAST_ACCESS_TIME` int(11) NOT NULL,
`OWNER` varchar(767) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`OWNER_TYPE` varchar(10) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`RETENTION` int(11) NOT NULL,
`SD_ID` bigint(20) DEFAULT NULL,
`TBL_NAME` varchar(256) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`TBL_TYPE` varchar(128) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`VIEW_EXPANDED_TEXT` mediumtext,
`VIEW_ORIGINAL_TEXT` mediumtext,
PRIMARY KEY (`TBL_ID`),
UNIQUE KEY `UNIQUETABLE` (`TBL_NAME`,`DB_ID`),
KEY `TBLS_N50` (`SD_ID`),
KEY `TBLS_N49` (`DB_ID`),
CONSTRAINT `TBLS_FK1` FOREIGN KEY (`SD_ID`) REFERENCES `SDS` (`SD_ID`),
CONSTRAINT `TBLS_FK2` FOREIGN KEY (`DB_ID`) REFERENCES `DBS` (`DB_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
TBLS table 스키마를 보게 되면 Location에 대한 컬럼이 보이지 않는 것을 알 수 있다.
그렇다면 각 테이블에 대한 Location은 어디에 있을까?
CREATE TABLE `SDS` (
`SD_ID` bigint(20) NOT NULL,
`CD_ID` bigint(20) DEFAULT NULL,
`INPUT_FORMAT` varchar(4000) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`IS_COMPRESSED` bit(1) NOT NULL,
`IS_STOREDASSUBDIRECTORIES` bit(1) NOT NULL,
`LOCATION` varchar(4000) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`NUM_BUCKETS` int(11) NOT NULL,
`OUTPUT_FORMAT` varchar(4000) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`SERDE_ID` bigint(20) DEFAULT NULL,
PRIMARY KEY (`SD_ID`),
KEY `SDS_N49` (`SERDE_ID`),
KEY `SDS_N50` (`CD_ID`),
CONSTRAINT `SDS_FK1` FOREIGN KEY (`SERDE_ID`) REFERENCES `SERDES` (`SERDE_ID`),
CONSTRAINT `SDS_FK2` FOREIGN KEY (`CD_ID`) REFERENCES `CDS` (`CD_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
예상하였듯 SDS Table 스키마에서 LOCATION 컬럼이 있는 것을 확인할 수 있다.
그렇다면 실제 테이블과 SDS 테이블의 정보를 어떻게 매칭 시킬 수 있을까?
그 비밀은 PRIMARY KEY인 SD_ID에 있다.
앞서 언급한 TBLS 테이블 스키마를 보면 DB_ID, SD_ID, TBL_NAME이 있는 것을 확인 할 수있다.
즉, 먼저 DBS에서 database 이름으로 DB_ID를 찾고, 그 DB_ID와 TBL_NAME을 이용하여 해당 테이블의 SD_ID를 찾을 수 있다.
그리고 이 SD_ID를 이용하면 SDS 테이블에서 해당 테이블에 대한 정보를 찾을 수 있는 것이다.
그리고 SDS의 LOCATION 값이 각 테이블에 show create table '테이블명' 을 했을때 볼 수 있는 LOCATION 값이다.
따라서 SDS의 LOCATION 값을 바꾸면 당연히 show craete table '테이블명' 을 헀을때 보이는 LOCATION 값도 바뀌게 된다.
SDS 테이블에는 각 Hive 테이블 하나 마다 정보가 존재하지만, 테이블의 Partition 마다도 존재합니다.
그렇기 때문에 각 Hive 테이블에 대한 정보인 SDS ROW 수는 테이블 수 + 전체 파티션 수 만큼 존재하는 것이 맞습니다.