PHPでビッグデータを操作しよう!Presto編 1

引き続きビッグデータ関連のミドルウェアを使った開発ばかりしてます。
もうすぐスーパーファミコンですね。

PHPを使ったビッグデータへのアプローチ方法などを話すことが多いですが、
今回は登壇時に例に挙げることも多いPrestoとPHPからの利用方法について紹介します。

speakerdeck.com

Prestoって何?

PrestoはFacebookで開発された分散SQLクエリエンジンで、
Webアプリケーションにとってはあまり馴染みがないかもしれませんが、
大規模なデータに対してインタラクティブに結果を返せるようになっていて、
Apache HiveやApache Impalaと同じようなものと考えるのが早いでしょう。

prestodb.io

ただWebアプリケーションエンジニアに嬉しいのは、
異なるデータベース(RDBMS)やデータストレージを結合して集計ができるという、
管理ツールや分散したデータベースを跨ぐ処理へのアプローチなど、活用できる場面が多くあります。
(Re:dashなどもあります)

更なる詳細については多くの記事や解説がありますので、そちらを参照してください。

tug.red

aws.amazon.com

blog.cloudera.co.jp

当然、利用ケースや場面によってはSparkやImpalaを利用した方が高速な場合もあり、
構成によっては、Kuduの方がよりフィットする場合もあると思います
(HDFSに格納されていてHBaseも使ってるぜ!みたいなケースだったり)

jp.cloudera.com

今回はPHPのアプリケーションで想定できるケースでどう使うか、
実際にどう組み込むかをやってみましょう。

異なるデータベースのjoinは、PostgreSQLのFDWやSQLServerのOPENQUERYなどもあります

F.31. postgres_fdw

Prestoをうごかす

現時点の最新は0.184ですので、マニュアルに沿ってインストールします。

$ wget https://repo1.maven.org/maven2/com/facebook/presto/presto-server/0.184/presto-server-0.184.tar.gz

解凍後に設置するだけで済みますが、
Prestoの設定や、接続したいデータベースの情報や記載するために
解凍したprestoのディレクトリに etc ディレクトリを作成します。

詳細はマニュアルに記載されていますので、ささっと初期設定をやってしまいます。

2.1. Deploying Presto — Presto 0.184 Documentation

launcherのrunを実行し、問題がなければstartなどで起動させます。

$ bin/launcher run

起動して、configで設定した discovery.uri にアクセスすると下記の画面が表示されます。

f:id:ytakezawa:20170915033255p:plain

接続先を追加する

次にMySQLに接続するように設定を追加します。

追加したetcディレクトリに、更に catalog ディレクトリを追加します。
各データベースへの接続情報はカタログに置いていきます。

MySQLのテーブル例

テーブルは下記のようなものを作ったとします。 (databaseはtestingとします)

CREATE TABLE `tests` (
  `test_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `test_name` varchar(85) NOT NULL DEFAULT '',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`test_id`),
  UNIQUE KEY `UIDX_TESTS_NAME` (`test_name`),
  KEY `IDX_TESTS` (`test_name`,`test_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

データは適当に入れておきます

INSERT INTO tests(test_name) VALUE('presto');

mysqlのcatalogは次の形式で記述します。

connector.name=mysql # コネクタのドライバ指定になるので、mysqlのままです
connection-url=jdbc:mysql://example.net:3306 # 以下は利用環境に合わせて指定します
connection-user=root
connection-password=secret

接続時にファイル名をカタログ名として利用しますので、
わかりやすい名前にしておきましょう。

仮にここでは etc/catalog/my_tests.properties として進めます。

Redisの例

次にRedisです。

せっかくなので、Redisのdatabaseをデフォルトではなく10を指定したとして設定を記述します。
10を利用するときは次の通りです

$ redis-cli
127.0.0.1:6379> SELECT 10
OK
127.0.0.1:6379[10]>

*IPなどは環境に合わせてください

Redisのcatalogに記述する項目は、先ほどのMySQLとは少し違います

5.14. Redis Connector — Presto 0.184 Documentation

今回はRedisの一番の基本のStringを主として、次のように記述します。

connector.name=redis
redis.table-names=test.string
redis.nodes=127.0.0.1:6379
redis.database-index=10

仮にここでは etc/catalog/red_tests.properties として進めます。

ここでも適当にデータを入れておきます。

127.0.0.1:6379[10]> SET presto awesome
OK
127.0.0.1:6379[10]> GET presto
"awesome"

これで準備が整いました。 prestoから接続できるか確認してみます。

Presto Command Line Interface

最初にインストールしたUIからはなにもできません。
CLIをダウンロードします。

2.2. Command Line Interface — Presto 0.184 Documentation

presto で利用できるようにjarのファイル名を変更します

$ wget https://repo1.maven.org/maven2/com/facebook/presto/presto-cli/0.184/presto-cli-0.184-executable.jar
$ mv presto-cli-0.184-executable.jar presto
$ chmod 755 presto

–serverにはprestoのconfigで設定した discovery.uri を指定します。

$ ./presto --server 127.0.0.1:8080
presto>

コンソールに入ったら現在時刻でも取得して動作確認しておきましょう。

presto> SELECT NOW();
               _col0
------------------------------------
 2017-09-15 04:29:51.689 Asia/Tokyo
(1 row)

Query 20170914_192951_00000_bv9h4, FINISHED, 1 node
Splits: 17 total, 17 done (100.00%)
0:01 [0 rows, 0B] [0 rows/s, 0B/s]

ばっちりです

MySQL, Redisに接続する

事前に記述した接続先に接続してクエリを投げてみましょう *launcherのrestartは忘れずに

MySQL

事前に用意した接続先にクエリを投げます

<catalog名>.<データベース名>.<テーブル名>で指定します。

presto> SELECT * FROM my_tests.testing.tests;
 test_id | test_name |       created_at
---------+-----------+-------------------------
       1 | presto    | 2017-09-15 03:49:30.000
(1 row)

Query 20170914_194006_00007_ijqj3, FINISHED, 1 node
Splits: 17 total, 17 done (100.00%)
0:01 [1 rows, 0B] [1 rows/s, 0B/s]

prestoから結果が取得できているのが見えます。

エラーが出る場合はSCHEMASが見えるか、など確認しましょう。

presto> SHOW SCHEMAS FROM my_tests;
       Schema
--------------------
 information_schema
 testing
(2 rows)

Redis

MySQLと問い合わせ方法は同じです。

redis.table-names で指定した名前を使って、<カタログ名>.<namesで記述した名前>を指定します。

マニュアルにもある通り、Redisは内部的なカラムとしていくつかあります。

Column name Type Description
_key VARCHAR Redis key.
_value VARCHAR Redis value corresponding to the key.

*一部抜粋

今はkey, valueをSELECT 区に入れてみます。

presto> SELECT _key, _value FROM red_tests.test.string;
  _key  | _value
--------+---------
 presto | awesome
(1 row)

Query 20170914_194457_00012_ijqj3, FINISHED, 1 node
Splits: 17 total, 17 done (100.00%)
0:00 [1 rows, 7B] [12 rows/s, 90B/s]

先ほど挿入した値が取得できました。

MySQL Redis JOIN

ことなるデータストレージを結合して検索してみましょう。

presto> SELECT _key, _value, test_id, test_name, created_at FROM my_tests.testing.tests AS myttt
     -> INNER JOIN red_tests.test.string AS redttt ON redttt._key = myttt.test_name
     -> WHERE myttt.test_name = 'presto';

  _key  | _value  | test_id | test_name |       created_at
--------+---------+---------+-----------+-------------------------
 presto | awesome |       1 | presto    | 2017-09-15 03:49:30.000
(1 row)

Query 20170914_195255_00018_ijqj3, FINISHED, 1 node
Splits: 66 total, 66 done (100.00%)
0:00 [2 rows, 7B] [4 rows/s, 15B/s]

当然存在しない値を指定すると、0rowsとなります。

presto> SELECT _key, _value, test_id, test_name, created_at FROM my_tests.testing.tests AS myttt
     -> INNER JOIN red_tests.test.string AS redttt ON redttt._key = myttt.test_name
     -> WHERE myttt.test_name = 'ytake';

 _key | _value | test_id | test_name | created_at
------+--------+---------+-----------+------------
(0 rows)

Query 20170914_195337_00020_ijqj3, FINISHED, 1 node
Splits: 66 total, 66 done (100.00%)
0:00 [1 rows, 7B] [2 rows/s, 15B/s]

次回は、早速PHPから利用し、もう少しだけ複雑にしてみます。