Google Cloudでも使えるHeatWave MySQL

本記事は [MySQL Advent Calendar 2024](https://qiita.com/advent-calendar/2024/mysql) の記事です。

# はじめに

HeatWave MySQLは、HeatWaveインメモリ・クエリ・アクセラレータを統合したMySQLクラウド・サービスです。他の分析データベースにETLでデータを連携させることなく、トランザクション・データのリアルタイム分析が可能となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/5b660e45-7d9d-a795-3867-12beb0425b9e.png)
（出典：[HeatWaveのOCI上でのアーキテクチャ](https://dev.mysql.com/doc/heatwave/en/mys-hw-architecture.html)）

> MySQLのクエリ・パフォーマンスを桁違いに向上させ、複雑さ、レイテンシ、リスク、コストを伴うETL（抽出、変革、ロード）を別の分析データベースに重複させることなく、トランザクション・データをリアルタイム分析します。データ・セキュリティを強化し、Oracle Cloud Infrastructure（OCI）、Amazon Web Services（AWS）、Microsoft AzureにHeatWave MySQL搭載アプリケーションを導入します。

（出典：https://www.oracle.com/jp/mysql/）

基本的にはOracle Cloud Infrastructure(OCI)上で利用可能となるMySQLのクラウドサービス(DBaaS)となりますが、HeatWave on AWSとしてAWSのリソースを利用してAWS上での利用も可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/64d8ddb2-5d30-dcf8-6eb1-861e6af83668.png)
（出典：[MySQL HeatWave on AWS](https://www.oracle.com/jp/developer/connecting-to-and-managing-heatwave-on-aws/)）

また、Azureでは ODSA(Oracle Database Service for Azure)としてAzure上からOCIのリソースを低レイテンシで利用することが可能となっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/d5a2598d-9fdc-d8d9-649c-f4cacb516eea.png)
（出典：[Oracle Database Service for Azure](https://www.oracle.com/jp/cloud/azure/oracle-database-for-azure/)）

AWSやAzureについての言及がされている一方で、Google Cloudについて特に言及されていません。
しかしながら、 [2024年6月に発表された](https://cloud.google.com/blog/ja/products/gcp/oracle-and-google-cloud-announce-multi-cloud-partnership) 通り、Google CloudとOCI間もクロスクラウド接続が可能となっており、低レイテンシ・大容量での通信を行うことが可能です。
したがって、このクロスクラウド接続を利用することでGoogle Cloudからも低レイテンシで HeatWave MySQL を利用することが可能です。

# 検証構成

今回は次図のようなクロスクラウド接続を構成し、Google Cloud上のVM(Compute Engine)からOCI上の HeatWave MySQL への接続を試みてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/1de3163a-1bdd-87c2-eb74-f8a498d60757.png)

Google CloudとOCIのクロスクラウド接続については別途記事を書いておりますので、そちらを参照ください。

- [Google Cloud から クロスクラウド接続で OCI のデータベースに接続してみた](https://qiita.com/asahide/items/fe0b33928b676dc1a760)

OCI側で HeatWave MySQL を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/7c1ba9d7-74a2-e288-1be6-5412408f57b1.png)

ネットワーク構成などを設定してHeatWave MySQLクラスタを作成します。
ハードウェアの構成で「HeatWaveクラスタの有効化」のチェックボックスを チェック することで、HeatWaveクラスタとして利用することが可能となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/77c9f5d2-e9ba-5b33-ce9e-3bf039009123.png)


数分待つと、DBシステムが作成完了します。この状態ではまだHeatWaveクラスタは作成中の状態です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/8621199a-bc7b-719f-5afb-e819c3766b0a.png)


さらにしばらく待つと、次図のようにHeatWaveクラスタもアクティブとなり作成完了した状態になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/8277c1ba-c835-18f4-2053-99a1e9428f18.png)


# HeatWave MySQL使ってみる

Google Cloudのコンピュートエンジンから、MySQL shellにて HeatWave MySQLに接続します。
HeatWave MySQLの接続タブで接続情報が確認できるため、そのエンドポイントとなるプライベートIPへ直接アクセスしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2313926/e34c9f56-b1d3-724c-64fc-96ed5c8682fa.png)

※事前にtpch用のオブジェクトの作成などを実施済みです

```SQL:MySQL Shellからの接続
$  mysqlsh -u root -p -h 192.168.1.62
Please provide the password for 'root@192.168.1.62': **********
Save password for 'root@192.168.1.62'? [Y]es/[N]o/Ne[v]er (default No): 
MySQL Shell 8.4.3

Copyright (c) 2016, 2024, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.

Type '\help' or '\?' for help; '\quit' to exit.
Creating a session to 'root@192.168.1.62'
Fetching global names for auto-completion... Press ^C to stop.
Your MySQL connection id is 185 (X protocol)
Server version: 9.1.1-u1-cloud MySQL Enterprise - Cloud
No default schema selected; type \use <schema> to set one.
 MySQL  192.168.1.62:33060+ ssl  SQL >  SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| mysql_audit        |
| mysql_option       |
| performance_schema |
| sys                |
| tpch               |
| world              |
| world_x            |
+--------------------+
9 rows in set (0.0027 sec)
 MySQL  192.168.1.62:33060+ ssl  SQL >  USE tpch;
Default schema set to `tpch`.
Fetching global names, object names from `tpch` for auto-completion... Press ^C to stop.
 MySQL  192.168.1.62:33060+ ssl  tpch  SQL >  SHOW TABLES;
+----------------+
| Tables_in_tpch |
+----------------+
| customer       |
| lineitem       |
| nation         |
| orders         |
| part           |
| partsupp       |
| region         |
| supplier       |
+----------------+
8 rows in set (0.0030 sec)
```

無事接続できました、HeatWaveの機能が利用できるかも確認してみます。
通常クエリ実行(**14.2791sec**)に比べるとHeatWave実行(**1.5147sec**)が大幅に高速化されていることが確認できます。

```SQL:通常クエリ実行

 MySQL  192.168.1.62:33060+ ssl  tpch  SQL >  SELECT /*+ SET_VAR(use_secondary_engine=OFF) */
                                          ->      l_returnflag,
                                          ->      l_linestatus,
                                          ->      SUM(l_quantity) AS sum_qty,
                                          ->      SUM(l_extendedprice) AS sum_base_price,
                                          ->      SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
                                          ->      SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
                                          ->      AVG(l_quantity) AS avg_qty,
                                          ->      AVG(l_extendedprice) AS avg_price,
                                          ->      AVG(l_discount) AS avg_disc,
                                          ->      COUNT(*) AS count_order
                                          ->  FROM
                                          ->      lineitem
                                          ->  WHERE
                                          ->      l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY
                                          ->  GROUP BY l_returnflag , l_linestatus
                                          ->  ORDER BY l_returnflag , l_linestatus;
+--------------+--------------+-------------+-----------------+-------------------+---------------------+-----------+--------------+----------+-------------+
| l_returnflag | l_linestatus | sum_qty     | sum_base_price  | sum_disc_price    | sum_charge          | avg_qty   | avg_price    | avg_disc | count_order |
+--------------+--------------+-------------+-----------------+-------------------+---------------------+-----------+--------------+----------+-------------+
| A            | F            | 37734107.00 |  56586554400.73 |  53758257134.8700 |  55909065222.827692 | 25.522006 | 38273.129735 | 0.049985 |     1478493 |
| N            | F            |   991417.00 |   1487504710.38 |   1413082168.0541 |   1469649223.194375 | 25.516472 | 38284.467761 | 0.050093 |       38854 |
| N            | O            | 74476040.00 | 111701729697.74 | 106118230307.6056 | 110367043872.497010 | 25.502227 | 38249.117989 | 0.049997 |     2920374 |
| R            | F            | 37719753.00 |  56568041380.90 |  53741292684.6040 |  55889619119.831932 | 25.505794 | 38250.854626 | 0.050009 |     1478870 |
+--------------+--------------+-------------+-----------------+-------------------+---------------------+-----------+--------------+----------+-------------+
4 rows in set (14.2791 sec)


```

```SQL:HeatWave実行
 MySQL  192.168.1.62:33060+ ssl  tpch  SQL > SELECT /*+ SET_VAR(use_secondary_engine=ON) */
                                          ->     l_returnflag,
                                          ->     l_linestatus,
                                          ->     SUM(l_quantity) AS sum_qty,
                                          ->     SUM(l_extendedprice) AS sum_base_price,
                                          ->     SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
                                          ->     SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
                                          ->     AVG(l_quantity) AS avg_qty,
                                          ->     AVG(l_extendedprice) AS avg_price,
                                          ->     AVG(l_discount) AS avg_disc,
                                          ->     COUNT(*) AS count_order
                                          -> FROM
                                          ->     lineitem
                                          -> WHERE
                                          ->     l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY
                                          -> GROUP BY l_returnflag , l_linestatus
                                          -> ORDER BY l_returnflag , l_linestatus;
+--------------+--------------+-------------+-----------------+-------------------+---------------------+-----------+--------------+----------+-------------+
| l_returnflag | l_linestatus | sum_qty     | sum_base_price  | sum_disc_price    | sum_charge          | avg_qty   | avg_price    | avg_disc | count_order |
+--------------+--------------+-------------+-----------------+-------------------+---------------------+-----------+--------------+----------+-------------+
| A            | F            | 37734107.00 |  56586554400.73 |  53758257134.8700 |  55909065222.827692 | 25.522005 | 38273.129734 | 0.049985 |     1478493 |
| N            | F            |   991417.00 |   1487504710.38 |   1413082168.0541 |   1469649223.194375 | 25.516471 | 38284.467760 | 0.050093 |       38854 |
| N            | O            | 74476040.00 | 111701729697.74 | 106118230307.6056 | 110367043872.497010 | 25.502226 | 38249.117988 | 0.049996 |     2920374 |
| R            | F            | 37719753.00 |  56568041380.90 |  53741292684.6040 |  55889619119.831932 | 25.505793 | 38250.854626 | 0.050009 |     1478870 |
+--------------+--------------+-------------+-----------------+-------------------+---------------------+-----------+--------------+----------+-------------+
4 rows in set (1.5147 sec)
```

実行計画からも差分を確認することが可能です。

```SQL: 通常クエリ実行(実行計画)

 MySQL  192.168.1.62:33060+ ssl  tpch  SQL > EXPLAIN SELECT /*+ SET_VAR(use_secondary_engine=OFF) */
                                          ->     l_returnflag,
                                          ->     l_linestatus,
                                          ->     SUM(l_quantity) AS sum_qty,
                                          ->     SUM(l_extendedprice) AS sum_base_price,
                                          ->     SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
                                          ->     SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
                                          ->     AVG(l_quantity) AS avg_qty,
                                          ->     AVG(l_extendedprice) AS avg_price,
                                          ->     AVG(l_discount) AS avg_disc,
                                          ->     COUNT(*) AS count_order
                                          -> FROM
                                          ->     lineitem
                                          -> WHERE
                                          ->     l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY
                                          -> GROUP BY l_returnflag , l_linestatus
                                          -> ORDER BY l_returnflag , l_linestatus;
+----+-------------+----------+------------+------+---------------+------+---------+------+---------+-------------------+----------------------------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered          | Extra                                        |
+----+-------------+----------+------------+------+---------------+------+---------+------+---------+-------------------+----------------------------------------------+
|  1 | SIMPLE      | lineitem | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 5845486 | 33.32999801635742 | Using where; Using temporary; Using filesort |
+----+-------------+----------+------------+------+---------------+------+---------+------+---------+-------------------+----------------------------------------------+
1 row in set, 1 warning (0.0033 sec)
```

```SQL: HeatWave実行(実行計画)

 MySQL  192.168.1.62:33060+ ssl  tpch  SQL > EXPLAIN SELECT /*+ SET_VAR(use_secondary_engine=ON) */
                                          ->     l_returnflag,
                                          ->     l_linestatus,
                                          ->     SUM(l_quantity) AS sum_qty,
                                          ->     SUM(l_extendedprice) AS sum_base_price,
                                          ->     SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
                                          ->     SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
                                          ->     AVG(l_quantity) AS avg_qty,
                                          ->     AVG(l_extendedprice) AS avg_price,
                                          ->     AVG(l_discount) AS avg_disc,
                                          ->     COUNT(*) AS count_order
                                          -> FROM
                                          ->     lineitem
                                          -> WHERE
                                          ->     l_shipdate <= DATE '1998-12-01' - INTERVAL '90' DAY
                                          -> GROUP BY l_returnflag , l_linestatus
                                          -> ORDER BY l_returnflag , l_linestatus;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                                                   |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------------------------------------------------------------+
|  1 | NONE        | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Using secondary engine RAPID. Use EXPLAIN FORMAT=TREE to show the plan. |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------------------------------------------------------------------+
1 row in set, 1 warning (0.0135 sec)
```


# まとめ

単純にGoogle CloudとOCIを接続して、Google CloudからOCI上のHeatWave MySQLをクエリしているだけなので当然と言えば当然なのですが、Google CloudからもHeatWave MySQLを利用することができました。
HeatWaveからBigQueryやVertex AIとの連携なども視野に入ってくると面白いかも知れません。


#### 参考情報

- [MySQLで高速分析を体験する](https://oracle-japan.github.io/ocitutorials/beginners/creating-HeatWave/)