hadoopアドベントカレンダー2011 6日目 Hiveの統計情報(続き)

クレジットカード現金化詐欺【業界人が教える口コミ情報】の12/6を担当するwyukawaです。ええ、書く人がいないので12/2に引き続いて書きます。ていうか明日も書く予定です。いい加減ネタつきるので書く人絶賛募集中です。

今日はHiveネタでいきます。

以前Hiveの統計情報 - wyukawa’s blogで少し書きましたが、Hive 0.7から統計情報を格納する機能が入っています。

hive-default.xmlに書かれている主な設定項目は下記のとおりです。

<property>
  <name>hive.stats.dbclass</name>
  <value>jdbc:derby</value>
  <description>The default database that stores temporary hive statistics.</description>
</property>

<property>
  <name>hive.stats.autogather</name>
  <value>true</value>
  <description>A flag to gather statistics automatically during the INSERT OVERWRITE command.</description>
</property>

<property>
  <name>hive.stats.jdbcdriver</name>
  <value>org.apache.derby.jdbc.EmbeddedDriver</value>
  <description>The JDBC driver for the database that stores temporary hive statistics.</description>
</property>

<property>
  <name>hive.stats.dbconnectionstring</name>
  <value>jdbc:derby:;databaseName=TempStatsStore;create=true</value>
  <description>The default connection string for the database that stores temporary hive statistics.</description>
</property>

Hiveが将来的にコストベースオプティマイザに対応するための布石なのだと思いますが、[HIVE-1361] table/partition level statistics - ASF JIRAだけHive 0.7にぽろっと入ったのが謎なんですよね。

や、入れるのはいいんですけどなぜデフォルトでオン(hive.stats.autogatherがtrue)なのかわかりませんねえ。。。

なぜなら統計データが保存されないからw

まあ順番に書いていきましょう。

統計情報を格納するDBのデフォルトはDerbyです。統計情報が何かって言うと行数です。ただし正確な値では無いですね。。。

ちなみに統計情報を格納するDBとHiveのメタデータを保存するDBは別の設定なので、後者をMySQLとかに変えても前者には影響ありません。

デフォルトの状態だとANALYZEコマンドもしくはINSERT文を発行した場合にもれなく統計情報が収集されます。ちなみにMySQLだとANALYZEは自動的によろしく実行されるみたいですね。

なのでderby.logとかTempStatsStoreフォルダができます。

が、統計データ自体は保存されませんw

PARTITION_STAT_TBLテーブルが統計情報を格納するテーブルですがジョブ実行後はデータは存在しません。
最後に削除してるからです。

DBをのぞいてみるとこんな感じ。

j バージョン 10.8
ij> connect 'jdbc:derby:;databaseName=TempStatsStore';
ij> show tables;
TABLE_SCHEM         |TABLE_NAME                    |REMARKS
------------------------------------------------------------------------
SYS                 |SYSALIASES                    |
SYS                 |SYSCHECKS                     |
SYS                 |SYSCOLPERMS                   |
SYS                 |SYSCOLUMNS                    |
SYS                 |SYSCONGLOMERATES              |
SYS                 |SYSCONSTRAINTS                |
SYS                 |SYSDEPENDS                    |
SYS                 |SYSFILES                      |
SYS                 |SYSFOREIGNKEYS                |
SYS                 |SYSKEYS                       |
SYS                 |SYSROLES                      |
SYS                 |SYSROUTINEPERMS               |
SYS                 |SYSSCHEMAS                    |
SYS                 |SYSSTATEMENTS                 |
SYS                 |SYSSTATISTICS                 |
SYS                 |SYSTABLEPERMS                 |
SYS                 |SYSTABLES                     |
SYS                 |SYSTRIGGERS                   |
SYS                 |SYSVIEWS                      |
SYSIBM              |SYSDUMMY1                     |
APP                 |PARTITION_STAT_TBL            |

21 行が選択されました
ij> select * from PARTITION_STAT_TBL;
ID                                                                                                                              |ROW_COUNT
-----------------------------------------------------------------------------------------------------------------------------------------------------

0 行が選択されました
ij> describe PARTITION_STAT_TBL;
COLUMN_NAME         |TYPE_NAME|DEC&|NUM&|COLUM&|COLUMN_DEF|CHAR_OCTE&|IS_NULL&
------------------------------------------------------------------------------
ID                  |VARCHAR  |NULL|NULL|255   |NULL      |510       |YES
ROW_COUNT           |BIGINT   |0   |10  |19    |NULL      |NULL      |YES

2 行が選択されました

ソース(JDBCStatsAggregator.java)には下記のようなコメントがあります。行数しか情報が無いから削除が妥当だと言ってます。よくわかりませんねw

      /* Automatic Cleaning:
          IMPORTANT: Since we publish and aggregate only 1 value (1 column) which is the row count, it
          is valid to delete the row after aggregation (automatic cleaning) because we know that there is no
          other values to aggregate.
          If ;in the future; other values are aggregated and published, then we cannot do cleaning except
          when we are sure that all values are aggregated, or we can separate the implementation of cleaning
          through a separate method which the developer has to call it manually in the code.
       */

ちなみにDerbyだとジョブを並列に実行して大丈夫かという不安があるかもしれないですが、これは大丈夫そうです。

ijでプロセスつかんだ状態でジョブを実行した場合

[Warning] could not update stats.

という警告はでますが統計情報収集以外の処理は問題ないです。

ドキュメントStatsDev - Apache Hive - Apache Software Foundationを見る限り統計情報を格納するDBはDerby, MySQLのどっちかが選べるようです。HBaseにも保存できるようですがDBじゃないのでここではちょっと無視しますね。

で、ためしにPostgreSQLでやると失敗しますw

<property>
  <name>hive.stats.dbclass</name>
  <value>jdbc:postgresql</value>
  <description>The default database that stores temporary hive statistics.</description>
</property>

<property>
  <name>hive.stats.jdbcdriver</name>
  <value>org.postgresql.Driver</value>
  <description>The JDBC driver for the database that stores temporary hive statistics.</description>
</property>

<property>
  <name>hive.stats.dbconnectionstring</name>
  <value>jdbc:postgresql://localhost:5432/hive?user=xxxxx&amp;password=xxxxxx</value>
  <description>The default connection string for the database that stores temporary hive statistics.</description>
</property>

とやっても

2011-12-05 15:43:00,230 ERROR jdbc.JDBCStatsPublisher (JDBCStatsPublisher.java:init(195)) - Error during JDBC initialization.
org.postgresql.util.PSQLException: 方法 org.postgresql.jdbc4.Jdbc4Statement.setQueryTimeout(int) はまだ装備されていませ
ん。
        at org.postgresql.Driver.notImplemented(Driver.java:753)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.setQueryTimeout(AbstractJdbc2Statement.java:668)
        at org.apache.hadoop.hive.ql.stats.jdbc.JDBCStatsPublisher.init(JDBCStatsPublisher.java:177)
        at org.apache.hadoop.hive.ql.exec.ExecDriver.execute(ExecDriver.java:652)
        at org.apache.hadoop.hive.ql.exec.MapRedTask.execute(MapRedTask.java:123)
        at org.apache.hadoop.hive.ql.exec.Task.executeTask(Task.java:130)
        at org.apache.hadoop.hive.ql.exec.TaskRunner.runSequential(TaskRunner.java:57)
        at org.apache.hadoop.hive.ql.Driver.launchTask(Driver.java:1063)
        at org.apache.hadoop.hive.ql.Driver.execute(Driver.java:900)
        at org.apache.hadoop.hive.ql.Driver.run(Driver.java:748)
        at org.apache.hadoop.hive.cli.CliDriver.processCmd(CliDriver.java:164)
        at org.apache.hadoop.hive.cli.CliDriver.processLine(CliDriver.java:241)
        at org.apache.hadoop.hive.cli.CliDriver.processReader(CliDriver.java:262)
        at org.apache.hadoop.hive.cli.CliDriver.processFile(CliDriver.java:269)
        at org.apache.hadoop.hive.cli.CliDriver.main(CliDriver.java:430)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.hadoop.util.RunJar.main(RunJar.java:186)
2011-12-05 15:43:00,240 WARN  mapred.JobClient (JobClient.java:copyAndConfigureFiles(649)) - Use GenericOptionsParser for parsing the arguments. Applications should implement Tool for the same.
2011-12-05 15:43:12,375 WARN  mapred.JobClient (JobClient.java:copyAndConfigureFiles(649)) - Use GenericOptionsParser for parsing the arguments. Applications should implement Tool for the same.
2011-12-05 15:43:22,615 ERROR jdbc.JDBCStatsAggregator (JDBCStatsAggregator.java:aggregateStats(106)) - Error during publishing aggregation. org.postgresql.util.PSQLException: ERROR: relation "partition_stat_tbl" does not exist
  ポジション: 28

というエラーが出てPARTITION_STAT_TBLテーブルを作れません。

理由はPostgreSQLJDBCドライバがjava.sql.Statement#setQueryTimeoutを実装していないためです。PostgreSQL

バージョンはpostgresql-9.0-802.jdbc4.jarです。ソース見る限りpostgresql-9.1-901.jdbc4.jarでもだめっぽい。

ちなみにPostgreSQLJDBCドライバのソースをちらみしたら

    /*
     * Sets the queryTimeout limit
     *
     * @param seconds - the new query timeout limit in seconds
     * @exception SQLException if a database access error occurs
     */
    public void setQueryTimeout(int seconds) throws SQLException
    {
        checkClosed();
        if (seconds < 0)
            throw new PSQLException(GT.tr("Query timeout must be a value greater than or equals to 0."),
                                    PSQLState.INVALID_PARAMETER_VALUE);

        if (seconds > 0)
            throw Driver.notImplemented(this.getClass(), "setQueryTimeout(int)");

        timeout = seconds;
    }

となっていたのでタイムアウトを0にすればいけるかなーと思って、

<property>
  <name>hive.stats.jdbc.timeout</name>
  <value>0</value>
  <description>Timeout value (number of seconds) used by JDBC connection and statements.</description>
</property>

としてみましたが同様のエラーが出ます。

これはHiveの統計情報機能の初期化処理(JDBCStatsPublisher#init)でデフォルトの30秒が使われてしまうからです。

これはちょっとHiveの実装がアレな気がしますw

connectメソッドのところのタイムアウトは差し替えられるのに。

ちなみに今度出るHive 0.8だと統計関連は3つのチケットがあがってます。これはどんな感じなんだろ。

Issue Navigator - ASF JIRA