린아저씨의 잡학사전

다음 내용은 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은 엄연히 다른 도메인이 되는 것이다.

 

(https://github.com/apache/hadoop/blob/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java#L711)

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 수는 테이블 수 + 전체 파티션 수 만큼 존재하는 것이 맞습니다. 

공유하기

facebook twitter kakaoTalk kakaostory naver band