X

Big Data、Data Integration、Data Lakeに関するテクノロジー、製品・サービス情報、セミナー情報などをお届けします

Big Data SQL - Schema on Read と Schema on Write

Schema on Read か Schema on Writeか

データロードについて。データロード先となるシステムは、通常2種類のうちどちらかです。2つのうち片方はSchema on Writeです。Schema on Writeでは、データロード時に列やデータフォーマットなどを定義する必要があります。ETL(データを幾つかの特定のシステム向けに使いやすいように変換する)が終わると、とても高速にREADでき、システムのパフォーマンスも良好です。しかし、留意すべき点は、データローディング時に既にペナルティを払っているということです。Schema on Writeのシステムとしては、Oracle DatabaseやMySQLのようなリレーショナル・データベースが挙げられます。

もう一つのアプローチはSchema on Readです。この場合、データに変更や変換を何も加えずにロードします。ETL処理をスキップするので(データを変換しない)、データフォーマットや構造に頭をかかえることもありません。ファイルを、ただ、ファイルシステム上にロードするだけです(写真をFlashカードや外部ストレージからノートPCにコピーするかのように)。データの意味付けはデータのREAD時に決定します。興味深いことに、ひとつのデータ(ファイル)は異なった作法で読まれます。例えばバイナリデータがあったとき、Serialization/Deserializationフレームワークを定義し、SELECT時に使って構造化データを得る、あるいは、バイトのセットを得る。他の例では、シンプルなCSVファイルであっても同じ列を数値とするか文字列とするか、その違いによって、ソートや比較演算で異なる結果になります。

HDFS(Hadoop Distributed File System)はSchema on Readの古典的な例です。Schema on ReadとSchema on Writeのさらに詳細はこちらで参照できます。

Schema on Writeは万能か?

ParquetやORCというHadoop内のファイルフォーマットについて聞いた方も多いでしょう。これはSchema on Writeアプローチの例です。ソースフォーマットを処理エンジン(hive, impala, Big Data SQLなど)にとって扱いやすいように変換します。Big Data SQLはPredicate Push Downや列のプルーニングなどのとても強力な機能を持ち、性能を大きく向上します。Parquetファイルを使ってBig Data SQLのドラマティックな性能改善することを、過去のBlogから、伝わってるといいのですが。しかし、データ変換の後、即座にソースファイルを削除しますか?いいえ、理由を説明しましょう。

ソースデータの変換

データソースとして、AVROフォーマットを使用します。

 

{
  "type" : "record",
  "name" : "twitter_schema",
  "namespace" : "com.miguno.avro",
  "fields" : [ {
    "name" : "username",
    "type" : "string",
    "doc" : "Name of the user account on Twitter.com"
  }, {
    "name" : "tweet",
    "type" : "string",
    "doc" : "The content of the user's Twitter message"
  }, {
    "name" : "timestamp",
    "type" : "long",
    "doc" : "Unix epoch time in seconds"
  } ]
}

このスキーマを使用してAVROファイルを作成します(3行持ちます)。

$ java -jar /usr/lib/avro/avro-tools.jar random --schema-file /tmp/twitter.avsc --count 3 example.avro

次に、このファイルをHDFSディレクトリ上に配置します。

$ hadoop fs -mkdir /tmp/avro_test/
$ hadoop fs -mkdir /tmp/avro_test/flex_format
$ hadoop fs -put example.avro /tmp/avro_test/flex_format 

メタデータを使って説明する準備ができましたね。CREATE HIVE TABLEでHive External Tableを作成します。

hive> CREATE EXTERNAL TABLE tweets_flex
ROW FORMAT
SERDE 'org.apache.hadoop.hive.serde2.avro.AvroSerDe'
STORED AS
INPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.avro.AvroContainerOutputFormat'
LOCATION '/tmp/avro_test/flex_format'
TBLPROPERTIES ('avro.schema.literal'='{
  "namespace": "testing.hive.avro.serde",
  "name": "tweets",
  "type": "record",
  "fields": [
 {"name" : "username",  "type" : "string", "default" : "NULL"},
 {"name" : "tweet","type" : "string", "default" : "NULL"},
 {"name" : "timestamp", "type" : "long",   "default" : "NULL"} ]
 }' );

Oracle Databaseからこのデータにアクセスできるように、上のHive表にリンクするようなOracleの外部表を作成します。

SQL> CREATE TABLE tweets_avro_ext
   (  
   username VARCHAR2(4000),
   tweet    VARCHAR2(4000),
  TIMESTAMP NUMBER
   ) 
   ORGANIZATION EXTERNAL 
    ( TYPE ORACLE_HIVE
      DEFAULT DIRECTORY "DEFAULT_DIR"
      ACCESS PARAMETERS
      ( com.oracle.bigdata.tablename=DEFAULT.tweets_flex)
)
   REJECT LIMIT UNLIMITED 
  PARALLEL;

ここで、Big Data SQL用に最適化のために、Parquetフォーマットに変換しようと思います。

hive> create table tweets_parq 
 ( username  string,
   tweet     string,
   TIMESTAMP smallint
   ) 
STORED AS PARQUET;

hive> INSERT OVERWRITE TABLE tweets_parq select * from  tweets_flex; 

このParquetファイルにリンクするような、Oracleの外部表を作成します。

SQL> CREATE TABLE tweets_parq_ext
   (  
   username VARCHAR2(4000),
   tweet    VARCHAR2(4000),
   TIMESTAMP NUMBER 
   ) 
   ORGANIZATION EXTERNAL 
    ( TYPE ORACLE_HIVE
      DEFAULT DIRECTORY "DEFAULT_DIR"
      ACCESS PARAMETERS
      ( com.oracle.bigdata.cluster=bds30
com.oracle.bigdata.tablename=DEFAULT.tweets_parq)
)
REJECT LIMIT UNLIMITED 
PARALLEL; 

OK、準備できたようです。では、作成した表(CREATE TABLE AS SELECTでAVROのデータから作成したので同一のデータを持つ表)にクエリを実行してみましょう。

SQL> select TIMESTAMP from tweets_avro_ext
WHERE
username='vic'
AND tweet='hello'
UNION ALL
select TIMESTAMP from tweets_parq_ext
WHERE
username='vic'
AND tweet='hello'
------------ 
1472648470
-6744

うーん、思ってたのと違う。データは同じであるべきなのに、何かがおかしい。これは、SMALLINTデータ型はTIMESTAMP(という名前の)列に対して十分ではないことが原因です。Hive内のParquet表を再作成し、再実行してみましょう。

hive> drop table tweets_parq;
hive> create table tweets_parq 
 (  
   username  string,
   tweet     string,
   TIMESTAMP bigint
   ) 
STORED AS PARQUET;
hive> INSERT OVERWRITE TABLE tweets_parq select * from  tweets_flex; 

Hive表再作成後は、Oracle Database側では何もする必要はありません。(Hive表の名前は変更していないので)

SQL> select TIMESTAMP from tweets_avro_ext
WHERE
username='vic'
AND tweet='hello'
UNION ALL
select TIMESTAMP from tweets_parq_ext
WHERE
username='vic'
AND tweet='hello'
------------ 
1472648470
1472648470

当たり!結果が等しくなりました。

結論

Schema on Read と Schema on Write どちらがよいか?それは哲学的問題です。Schema on Readは柔軟性と人的エラーからの保護をもたらします。Schema on Writeはよりよい性能をもたらします。一般的に、ソースフォーマットのデータを(念の為)保持しておくこと、そしてスキャンするエンジンにとって便利な別のフォーマットに最適化することは、良い考えです。ETLは誤った変換をするかもしれません。ソースデータがあれば、データを正しいやり方でソースから再パースすることができます。

本投稿は   Big Data SQL Quick Start. Schema on Read and Schema on Write - Part11. を元に投稿しています。

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.