DatastoreのデータをCloudSQLに入れるまで

タイトルの通り、Datastoreに入れていたデータをCloudSQL(MySQL)に持っていくためにやったことです。

# 結論
Datastore ---(export)---> GCS ---(import)---> BQ ---(クエリ結果取得)---(import)----> CloudSQL


# 概要
### Datastoreからのエクスポート
サポートされているエクスポート方法はGCSへの出力のみです。LevelDBというkey-value形式でエクスポートされます


### DatastoreからBQへ
この手順については[公式ドキュメント](https://cloud.google.com/bigquery/docs/loading-data-cloud-datastore?hl=ja)にもあり、GCSへのエクスポート、BQでGCSからのインポートによるテーブル作成という流れになります。

### DatastoreからCloudSQLへ
残念ながら、それを一発でやるような方法やドキュメントはありません。そもそもNoSQLのDatastoreからCloudSQLに入れるということが必要になるケースが少ないかもしれませんね。
仕方ないので、BQでクエリ実行して取得した結果をCSVとして保存し、それをMySQLにインポートするという手順を採りました。

### 全体の流れ
![qiita_datastore2cloudsql.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/8731363b-00bb-7671-b912-b9650efbeb37.png)


# 手順
### 前提
 - Datastoreは、Datastoreモードで使用しています。
 - エクスポートそれ自体において必須なわけではないですが、CloudSQLにアクセスするための踏み台GCEを経由するするようにしています。

### 0. 事前準備
 - GCSバケットの作成(リージョンに注意: インポート先BQとリージョンを合わせる必要があります)
 - BQデータセットの作成(リージョンに注意: GCSバケットとリージョンを合わせる必要があります)
 - CloudSQLインスタンスおよびDBの作成
 - CloudSQLにアクセスできる環境準備(ここではGCEインスタンスを用意しますが、ローカルマシンでも問題ありません)

### 1. DatastoreからGCS
ちなみにこのようなサンプルデータを用意しています。
![datastore_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/618532f9-6d3a-b922-f170-bb85b9d1265b.png)

Datastore > インポート/エクスポートで「エクスポート」を選択します。
エクスポートするKindおよびエクスポート先のGCSバケットを選択して、エクスポートを実行します。
![2_datastore_to_gcs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/196bfac8-d543-e52d-2291-0638db835dde.png)

エクスポートが完了すると、GCSには「`${YYYY-MM-DDTHH:MM:SS}`_`${自動採番num}`」のフォルダが作成されます。
![3_datastore_exported.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/d0843520-af97-8332-1cd2-184196393745.png)

### 2. GCSからBQ
Big Query > SQLワークスペースで、データセットを開き、[テーブルを作成]をクリックします。

![5_import_to_bq.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/6854c520-adeb-ff39-91f7-c1bc0fbfa3a4.png)

テーブルの作成ページでの選択は以下の通りです(記載のないものはデフォルト)

[ソース]
テーブルの作成元 : Cloud Storage
GCSバケット府からファイルを選択: [作成されたバケット]/[namespace名]/[kind名]/[namespace名]_[kind名].export_metadata
ファイル形式 Cloud Datastore バックアップ

[送信先]
テーブル: 任意のテーブル名

エクスポートが成功すると、BigQueryのプレビュー等で、実際にデータが確認できます。
![6_bq_imported.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/98950796-1597-a2be-f462-4d610972695a.png)


### 3.BQでクエリ結果取得

この結果を取得するためにクエリを実行します。
Datastoreにより用意されたカラムもあるので、実際にCloudSQLに入れるのに必要なデータのみ抽出します。

ここでは以下のようになりました

```
 SELECT  hoge, fuga, piyo FROM `MY-GCP-PROJECT.bq_datastore_export_test.table_datastore_export_test`
```


![7_bq_query.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/528dbb39-b24d-0c71-c78e-c682794bfa5d.png)

### 4.クエリ結果の出力
ここではCloudShellを用いてクエリ実行とGCSへのエクスポートを行います
出力先GCSバケットは、datastoreのexport先と同じにしています。クエリは、3の手順のものと同一です。

```
 bq query --nouse_legacy_sql 'EXPORT DATA OPTIONS(
uri="gs://ryoeguchi-datastore-export/query_export_*.csv",format="CSV",overwrite=true,header=true,field_delimiter=",")
AS SELECT hoge, fuga, piyo FROM `MY-GCP-PROJECT.bq_datastore_export_test.table_datastore_export_test` '
```

GCSバケットにcsvファイルが出力されいるのが確認できます。

![8_query_export_to_gcs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/09baaff5-f29d-8f8e-ecb8-4fb601239d07.png)

### 5.クエリ結果をCloudSQLへ投入

gsutilコマンドで、CloudSQLに接続するマシン(GCEインスタンスまたはローカルマシン)に、クエリ結果をコピーします。

```
gsutil cp gs://ryoeguchi-datastore-export/query_export_*.csv . 
```

CloudSQLに接続し、importを実施します。

```
sudo mysql -h IP.OF.CLOUD.SQL -u root -p -D datastore_imported
```
CloudSQLのインポート先テーブルはこのように作りました(bqクエリ結果とカラムの順序の整合を取る必要あり)。

```
 create table table_datastore_import(hoge varchar(255), fuga int, piyo varchar(255));
```

データが登録されていないテーブルが作成されています。

```
 select * from table_datastore_import;  
 Empty set (0.002 sec)
```

load data機能を用いて、CSVファイルを取り込みます。bqの出力時に、区切り文字はカンマ',', ヘッダ出力はtrueにしたので、それに合わせてオプションは設定しています。

```
load data local infile 'query_export_000000000000.csv' into table table_datastore_import fields terminated by ',' enclosed by '"' ignore 1 lines;
Query OK, 3 rows affected (0.008 sec)
 Records: 3  Deleted: 0  Skipped: 0  Warnings: 0
```

データが投入されていることが確認できました。

```
 select * from table_datastore_import;
+-------------+------+-----------------+
| hoge        | fuga | piyo            |
+-------------+------+-----------------+
| hello world |    2 | hello datastore |
| text_hoge   |    1 | piyopiyo        |
| hogehoge    |    4 | piyopiyo        |
+-------------+------+-----------------+
3 rows in set (0.002 sec)
```

以上で、DatastoreからCloudSQLへデータをインポートすることができました。

### 余談
私が過去にこの操作を行った際、コンソール上でクエリ実行結果をローカルに保存してそれを踏み台サーバに送るようにしていました。bqコマンドによりGCSに実行結果を保存するようにすれば、Datastoreに入っている情報をGCPの外(ローカルマシン)に出さずにも出来るようになるため、より安全ですね。

コンソール上でのクエリ結果保存先指定は、クエリ結果の[結果の保存]から実施可能です。
![x1_query_result_to_local.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2345258/d3788fb3-a92c-3f7c-19ab-8f8d819a0ca9.png)

# 参考
 - [Datastore エクスポートからのデータの読み込み | Big Query | Google Cloud](https://cloud.google.com/bigquery/docs/loading-data-cloud-datastore?hl=ja)
 - [bqコマンドでクエリ結果をGCSに保存(Bigquery) - Qiita ](https://qiita.com/kenjiron/items/e1e708f2471cfd01f3f8)