BigQuery Cloud SQL federated queryでRDSのデータにクエリを実行してみる

[BigQueryからCloudSQLに直接クエリが実行](https://cloud.google.com/bigquery/docs/cloud-sql-federated-queries)できるようになったということですが、サービスの基盤はAWSを利用しているためDBはもちろんRDS。。。
そこでどうにかしてRDSに溜まっているデータに対してBigQueryから直接クエリを実行したいと思いやってみました。
正確には、RDSをCloudSQLでレプリケーションしてCloud SQL federated queryでBigQueryからクエリを実行してみました。
# 構成
![無題のプレゼンテーション (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/d49e3c9d-ca80-5309-431c-4698fa72ae9d.png)
既にソースDBに対して3台のリードレプリカが作成されている状態だったので、こんな感じの構成にしていきたいと思います。
# CloudSQLレプリケーション設定
外部サーバーからCloudSQLレプリカに複製する場合いくつか[要件](https://cloud.google.com/sql/docs/mysql/replication/replication-from-external?hl=ja)があるのでそれを満たしているか確認します。
>* GTID が有効にされていて、GTID 整合性が強制されること。

問題はここでした。
[AWSのドキュメント](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/mysql-replication-gtid.html#mysql-replication-gtid.configuring-existing-read-replicas)を見ると
>DB インスタンスまたはリードレプリカで RDS MySQL バージョン 5.7.22 以下を使用している場合は、DB インスタンスまたはリードレプリカをアップグレードします。RDS MySQL バージョン 5.7.23 以降の MySQL 5.7 バージョンにアップグレードします。

:thinking:
![Screenshot_2019-08-15 RDS · AWS Console(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/66260269-9c18-d0ea-39c0-a0f65e27a419.png)
:sweat_smile:
稼働中DBのバージョンを勝手にあげる訳にはいかないので、一先ずやりたいことが出来るか検証用の環境を作って試してみます。
### RDS設定

まずパラメータグループの作成を行います。
![Screenshot_2019-08-16 RDS · AWS Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/9afd4b6d-1ce8-b1d6-03c7-af99e384b167.png)

| 名前 | 値 |
|:-:|:-:|
|  enforce_gtid_consistency | ON  |
|  gtid-mode | ON  |

GTIDを使用したレプリケーションを構成するために、このように変更します。


ついでに、パブリックアクセシビリティを【はい】に変更しCloudSQLから接続できる状態にします。
![Screenshot_2019-08-15 RDS · AWS Console(5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/c1bd08bf-2d9b-3653-49d7-08865fae3f2a.png)

RDS外部からレプリケーションを行うのでバイナリログ保持時間が適切に設定されているか確認しておきます。

```
mysql> call mysql.rds_show_configuration;
+------------------------+-------+------------------------------------------------------------------------------------------------------+
| name                   | value | description                                                                                          |
+------------------------+-------+------------------------------------------------------------------------------------------------------+
| binlog retention hours | NULL  | binlog retention hours specifies the duration in hours before binary logs are automatically deleted. |
+------------------------+-------+------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
```

NULLに設定されているとバイナリログはできる限り早く消去されていくとのことなので、一先ず24時間保存するように変更します。

```
mysql> call mysql.rds_set_configuration('binlog retention hours', 24);
```

ソースDBにmysqldumpを実行しても良いのですが、後に本番環境でやることを考えてリードレプリカの一台にmysqldumpを実行したいと思います。
念の為リードレプリカのレプリケーションを停止しておきます。

```
mysql> CALL mysql.rds_stop_replication;
```

[GCPのドキュメント](https://cloud.google.com/sql/docs/mysql/replication/replication-from-external?hl=ja)にコマンドが記載されているのですが、そのままだとエラーになるので`--master-data=1`は省きます。
一つのコマンドでGCSにアップロードをしても良いのですが、今回は一度サーバー上に保存します。

```
$ mysqldump \
    -h [MASTER_IP] -P [MASTER_PORT] -u [USERNAME] -p \
    --databases [DBS]  \
    --hex-blob  --skip-triggers \
    --order-by-primary --no-autocommit \
    --ignore-table [VIEW] \
    --single-transaction --set-gtid-purged=on > ./dump.sql
```

mysqldumpが終了したらGCSにアップロードします。

```
$ gsutil cp ./dump.sql gs://[BUCKET]/[PATH_TO_DUMP]
```

### CloudSQL設定
ここからやっとGCPをいじり始めます。
![Screenshot_2019-08-15 SQL の概要 - dip-skylab - Google Cloud Platformのコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/6f9c4422-ccae-e376-4b35-131adc545529.png)
CloudSQLからデータ移行を選択します。
移行を開始を選択して入力が必要な部分を埋めていきます。
![Screenshot_2019-08-15 SQL の概要 - dip-skylab - Google Cloud Platform(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/5255f339-f4cc-7516-fd76-654d28a75ba7.png)

ソースのパブリックIPアドレスについては、nslookupでホストを指定して調べました。
レプリケーションユーザーですが、今回はCloudSQLのIPからのみ接続を許可したいのでRDS側には後ほどユーザーを作成したいと思います。
__証明証ですが、[こちら](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/CHAP_MySQL.html#MySQL.Concepts.SSLSupport)にダウンロードリンクが記載されているので入手します。__
ダウンロードした証明書を指定し、【次へ】を選択。
そして__[BigQueryのデータロケーション](https://cloud.google.com/bigquery/docs/locations?hl=ja&_ga=2.88534355.-994852253.1565746446)と同じゾーン__・任意のマシンタイプを選択、先ほどGCSにアップロードしたダンプファイルを指定しリードレプリカの作成をします。
### CloudSQLからRDSへの接続設定
__[GCPドキュメント](https://cloud.google.com/sql/docs/mysql/replication/replication-from-external?hl=ja)にもある通り、この作業はCloudSQLでリードレプリカの作成をしてから30分以内に行わないとリードレプリカの作成が停止します。__
ということで、CloudSQLのリードレプリカ作成オペレーションの終了を待たずに並行して作業を進めていきます。
まずCloudSQLリードレプリカのIPアドレスを調べます。

```
$ gcloud sql instances describe [REPLICA_NAME] --format="default(ipAddresses)"
ipAddresses:
- ipAddress: xxx.xxx.xxx.xxx
  type: PRIMARY
- ipAddress: xxx.xxx.xxx.xxx
  type: OUTGOING
```

IPアドレスを取得できるようになるまで数分かかります。
CloudSQL(OUTGOINGのIP)からソースデータベースに接続できるようにRDS側でセキュリティグループ等を設定します。
次にレプリケーションユーザーを作成します。
既に作成している場合は飛ばしてください。

```
mysql> CREATE USER 'cloudsql_rep'@'OUTGOINGのIP' IDENTIFIED BY 'パスワード';
mysql> GRANT REPLICATION CLIENT, REPLICATION SLAVE ON *.* TO 'cloudsql_rep'@'OUTGOINGのIP';
```
ここまで設定したらCloudSQLにダンプファイルが復元されるまで待ちます。

CloudSQLでリードレプリカの作成が終了したら一応確認します。

```
mysql> SHOW SLAVE STATUS ¥G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: xxx.xxx.xxx.xxx
                  Master_User: cloudsql_rep
                  Master_Port: 3306
                Connect_Retry: 60
...
```

無事レプリケーションができているようです。
mysqldumpを実行するためにレプリケーションを停止したリードレプリカも忘れずに再開しておきます。

```
mysql> CALL mysql.rds_start_replicartion;
```

# BigQuery 接続を作成
いよいよ大詰めです。
BigQueryのコンソールから接続の作成を選択し、
![Screenshot_2019-08-16 BigQuery - dip-skylab - Google Cloud Platformのコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/4905be8f-86fe-7cef-0a3c-b875c3f9907e.png)
必要な情報を入力していきます。
![Screenshot_2019-08-16 BigQuery - dip-skylab - Google Cloud Platform(1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/b521f32b-3598-f503-4fae-5a3d151b143b.png)
接続を作成を選択すると・・・
![Screenshot_2019-08-16 BigQuery - dip-skylab - Google Cloud Platform(2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/75da792a-97ef-d504-2135-c28c70a421f5.png)
外部接続が現れました！
取り敢えずクエリを実行してみます。

```
SELECT * FROM EXTERNAL_QUERY("プロジェクトID.ゾーン.CloudSQL_federated_query_RDS", "SELECT * FROM INFORMATION_SCHEMA.TABLES;");
```

![Screenshot_2019-08-16 BigQuery - dip-skylab - Google Cloud Platform(3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408616/1e30eaaf-06d0-4ed8-5fac-ff1bff62087d.png)
結果が返ってきました！

# さいごに
残念なことに本番環境のRDSはMySQLのバージョンが要件を満たせていなかったため検証用に環境を作って試してみましたが、やりたいことはできそうなので気づかれないように本番環境のバージョンを上げておきたいと思います。
Cloud SQL federated queryについてですが、CloudSQLに対してクエリを実行しているのでBigQueryほどのスピードにはなりません。
また、クエリ結果もキャッシュされないようなのであまり調子に乗りすぎると気づかぬうちにスキャン量が積み重なってしまうかもしれません。。。
ただ、いろいろな可能性を感じられるのでいじり倒してみたいと思います。