TDSQL-Cで実現する真のDBスケーリング（あるいはその夢）

# はじめに：スマホゲームのインフラ最適化事情
スマホゲームのインフラはゲーム内の状況に応じて負荷が激しく変動する点が特徴として挙げられます。例えば、GvGやイベント開始時などは多くのユーザさんが同時にゲームを遊ぶため高負荷になります。逆に深夜帯など遊ぶ人が少ない時間帯はアクセスが極端に減ります（ゼロにはなりませんが）
ピーク時の負荷に合わせたサイジングで運用するとインフラ費用が嵩み、ゲームタイトルの運営上大きな負担となってしまいます。かといってリソースを切り詰めすぎてしまうと性能低下によるプレイフィール悪化としてサービス影響が出てしまい、ユーザ離脱に繋がります。私個人としては「性能とコストのバランスに優れた柔軟なサイジング設計」こそがスマホゲームのインフラ最適化における最重要事項だと考えています。

## フロント層（APIなど）のサイジングは最適化手法が成熟しつつある
スマホアプリから直接通信を受けるAPI類を受け持つフロント層はIaaSのオートスケーリングの機能を活用して最適なサイジングを行う設計が主流となっています。また、最近ではk8sなどコンテナ技術を活用して効率よくリソースを利用するアプローチも一般的になってきました。フロント層は5年程前に比べて柔軟なサイジングが劇的に進化した領域だと言えます。
![screenshot.58.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2127401/81f2d281-9e51-f503-363d-2c4d3a211b78.jpeg)

## ではバックエンド層（DBなど）はどうか？
一般的にゲームインフラの費用内訳を見るとフロント層以外ではバックエンド層、特にDBが大きな割合を占めるケースが多いです。スマホゲームのバックエンドとして採用が多い(※)MySQL互換のAurora(AWS)などマネージドサービスは導入・運用が比較的容易ですが、適切にサイジングしないと費用が高額になる傾向があると言えます。また、アーキテクチャ上の理由からフロント層程の柔軟性を確保出来ず、まだまだ最適化余地が残された領域だと考えています。
※マイネット調べ

# MySQLのスケーリング戦略
MySQLは従来より、ソース（旧称：マスタ）からレプリケーションを通じて同期されたレプリカ（同スレーブ）を増台する事で読み込み性能をスケールアウトする戦略を主としています。レプリケーションを介さずコンピューティング層とストレージ層を分離したクラスタアーキテクチャを採用するAuroraや、今回取り上げるTDSQL-Cでも基本的には同様です。

## Read性能のスケーリング
レプリカをスケールイン/アウトで台数調整する事によりSELECT文に対する性能は比較的リニアにスケーリングできます。Auroraではec2同様にオートスケール設定が提供されており、Read要求が増えた際はレプリカをスケールアウトし、落ち着いたらスケールインを走らせる事で常時最適なサイジングで運用する事が可能です。
アプリケーションからのアクセスは、エンドポイント形式で提供されるProxy機能によりスケールイン/アウトを追従してリクエストを分散する事が可能で、これによりダウンタイムのないスケーリングが提供されています。

## Write性能は？
MySQLは基本的にシングルソースを前提としているため、一部例外を除いてスケールアップ/ダウンで対応する事になります。しかしスケールイン/アウトとは異なり、スケールアップ/ダウンではソースインスタンスのスペック変更に伴うダウンタイムが発生するため、実施の際はゲームをメンテナンス状態に切り替えた上でユーザさんのプレイに影響しない状態を担保する必要があります。そのためゲーム内状況に応じた頻繁かつ柔軟なサイジングを行う事は困難で、定期的な見直しに基づく実施に留まるケースが殆どだと思います。
![screenshot.65.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2127401/08422ae3-8210-c6b0-d19e-5679308b89dc.jpeg)

# ソースインスタンスの無停止スケールアップ/ダウンへの道
そんなわけで長らくWrite性能のスケーリングはゲームインフラの最適化上の課題でした。が、最近tencent cloudで提供開始されたばかりのTDSQL-Cが超高速なスケーリング機能を売りとしているので試してみました。
> Fast and Flexible Configuration Adjustment
Compute nodes can be quickly upgraded/downgraded according to business needs without causing disconnections. Upgrade/Downgrade can be completed within seconds at the soonest subject to the memory size, which optimizes the costs of computing resources.
[Cloud Native Database TDSQL-C > Product Introduction > Strengths](https://intl.cloud.tencent.com/jp/document/product/1098/40616)

## そもそもTDSQL-Cってなに？
> Cloud Native Database TDSQL-C(TDSQL-C)は、Tencent Cloudが自社開発した新世代の高性能で可用性の高いエンタープライズレベルの分散型クラウドデータベースです。従来のデータベースやクラウドコンピューティングと新しいハードウェアテクノロジーの優位性を統合することによって、MySQLおよびPostgreSQLと100%の互換性があり、100万QPSを超える高スループット、128TBという大容量の分散型インテリジェントストレージを実現し、データのセキュリティと信頼性を確保します。
[クラウドネイティブデータベース TDSQL-C | Tencent Cloud](https://intl.cloud.tencent.com/jp/product/cynosdb?lang=jp)

すごく乱暴な説明ですが「tencent cloud上で提供されるAuroraのようなもの」をイメージして良いと思います。従来はCynosDBの名称で開発されており、2021年8月から日本リージョンで提供開始となりました。旧名称の頃に中国リージョンで試用させてもらい、個人的に日本リージョンへの導入を心待ちにしていたプロダクトです。
（競合プロダクトの試用もまとめて発表した際の資料はこちら：[新興クラウドのMySQL互換DBaaS徹底比較！](https://www.mysql.com/jp/why-mysql/presentations/dbts-online-2020-mysql-mysql-daas-20201210-doc-jp/))

# TDSQL-C vs Aurora スケールアップ/ダウン時のダウンタイム比較
前置きが長くなりましたが、ここからは実際にプロダクトを利用してスケーリングにおけるダウンタイム計測をしていきます。

:::note warn
今回はコンパクトにまとめるため、簡易的な方法で検証しています。
実際にサービスでの利用を検討する際はその特性に応じた検証を行う事をオススメします。
:::

## 計測方法
0.5秒間隔でmysqlcliでDB接続→insert→selectを繰り返しながら裏側でコンソールからスケーリング処理を走らせてみます。
テストスクリプト

```
#!/bin/bash
while sleep 0.5
do
   mysql -u<username> -p<password> -h<endpoint> -e "insert into scale_logging (buffer_pool,createtime)values(@@innodb_buffer_pool_size,now());select max(id) from scale_logging;" <dbname>
done
```

記録用テーブル

```
CREATE TABLE `scale_logging` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `buffer_pool` bigint(20) DEFAULT NULL, #スケールアップ/ダウンの完了を確認するために@@innodb_buffer_pool_sizeの値を格納
  `createtime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
```

## Aurora(パターン1:クラスタエンドポイント利用)
5.7.mysql_aurora.2.07.2 - 1Writer/1Reader(Multi-Az)で計測
※先にリーダーをスケールアップ/ダウンした後にF/Oを発生させています

### db.t3.small -> db.t3.mediumスケールアップ

```
+------+-------------+---------------------+
| id   | buffer_pool | createtime          |
+------+-------------+---------------------+
| 2511 |   660602880 | 2021-10-06 03:26:59 |
| 2512 |   660602880 | 2021-10-06 03:26:59 |
| 2513 |   660602880 | 2021-10-06 03:27:00 |  ←コンソール上でスケールアップ実施
| 2514 |   660602880 | 2021-10-06 03:27:00 |
・・・
| 2764 |   660602880 | 2021-10-06 03:29:25 |
| 2765 |   660602880 | 2021-10-06 03:29:25 |
| 2766 |   660602880 | 2021-10-06 03:29:26 |　←リーダーへのF/O開始
| 2767 |  1586495488 | 2021-10-06 03:29:38 |　←リーダーへのF/O終了（innodb_buffer_pool_size増加）
| 2768 |  1586495488 | 2021-10-06 03:29:38 |
+------+-------------+---------------------+
```
→ダウンタイムは**12秒**。ターミナル上は24回"ERROR 2003 (HY000): Can't connect to MySQL server on <cluster-endpoint>(111)"が表示された。

### db.t3.medium -> db.t3.smallスケールダウン

```
+------+-------------+---------------------+
| id   | buffer_pool | createtime          |
+------+-------------+---------------------+
| 2892 |  1586495488 | 2021-10-06 03:49:59 |
| 2893 |  1586495488 | 2021-10-06 03:50:00 | ←コンソール上でスケールアップ実施
| 2894 |  1586495488 | 2021-10-06 03:50:01 |
・・・
| 3064 |  1586495488 | 2021-10-06 03:51:37 |
| 3065 |  1586495488 | 2021-10-06 03:51:38 | ←リーダーへのF/O開始
| 3066 |   660602880 | 2021-10-06 03:51:53 | ←リーダーへのF/O終了（innodb_buffer_pool_size減少）
| 3067 |   660602880 | 2021-10-06 03:51:53 |
| 3068 |   660602880 | 2021-10-06 03:51:54 |
+------+-------------+---------------------+
```
→ダウンタイムは**14.5秒**。ターミナルではERROR 2003 x 29回。

## Aurora(パターン2:RDS Proxy利用)
パターン1同様だが-hにRDS Proxyのエンドポイントを指定

### db.t3.small -> db.t3.mediumスケールアップ

```
+------+-------------+---------------------+
| id   | buffer_pool | createtime          |
+------+-------------+---------------------+
| 3109 |   660602880 | 2021-10-06 05:00:59 |
| 3110 |   660602880 | 2021-10-06 05:01:00 |  ←コンソール上でスケールアップ実施
| 3111 |   660602880 | 2021-10-06 05:01:00 |
・・・
| 3339 |   660602880 | 2021-10-06 05:03:11 |
| 3340 |   660602880 | 2021-10-06 05:03:11 |
| 3341 |   660602880 | 2021-10-06 05:03:12 |　←Proxyの接続先変更開始
| 3342 |  1586495488 | 2021-10-06 05:03:14 |　←Proxyの接続先変更終了（innodb_buffer_pool_size増加）
| 3343 |  1586495488 | 2021-10-06 05:03:14 |
+------+-------------+---------------------+
```
→ダウンタイムは**1.5秒**。ターミナル上はエラー表示なし（接続待ちの状態があったと推測される）

### db.t3.medium -> db.t3.smallスケールダウン

```
+------+-------------+---------------------+
| id   | buffer_pool | createtime          |
+------+-------------+---------------------+
| 3656 |  1586495488 | 2021-10-06 05:34:59 |
| 3657 |  1586495488 | 2021-10-06 05:35:00 |  ←コンソール上でスケールアップ実施
| 3658 |  1586495488 | 2021-10-06 05:35:00 |
・・・
| 3915 |  1586495488 | 2021-10-06 05:37:25 |
| 3916 |  1586495488 | 2021-10-06 05:37:26 |
| 3917 |  1586495488 | 2021-10-06 05:37:27 |　←Proxyの接続先変更開始
| 3918 |   660602880 | 2021-10-06 05:37:28 |　←Proxyの接続先変更終了（innodb_buffer_pool_size減少）
| 3919 |   660602880 | 2021-10-06 05:37:29 |
+------+-------------+---------------------+
```
→ダウンタイムは**1秒**。ターミナル上は"ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query"が一度だけ表示された。

## TDSQL-C
Compatible Database：MYSQL 5.7 - Database Version：2.0.14 - Single Instanceで計測

### 2-core/4GB MEM -> 2-core/8GB MEMスケールアップ

```
+-----+-------------+---------------------+
| id  | buffer_pool | createtime          |
+-----+-------------+---------------------+
|  91 |  2147483648 | 2021-10-06 11:53:59 |
|  92 |  2147483648 | 2021-10-06 11:54:00 | ←コンソール上でスケールアップ実施
|  93 |  2147483648 | 2021-10-06 11:54:00 |
|  94 |  2147483648 | 2021-10-06 11:54:01 |
|  95 |  2147483648 | 2021-10-06 11:54:01 |
|  96 |  2147483648 | 2021-10-06 11:54:02 |
|  97 |  2147483648 | 2021-10-06 11:54:02 |
|  98 |  2147483648 | 2021-10-06 11:54:03 |
|  99 |  2147483648 | 2021-10-06 11:54:03 |
| 100 |  2147483648 | 2021-10-06 11:54:04 |
| 101 |  2147483648 | 2021-10-06 11:54:04 |
| 102 |  2147483648 | 2021-10-06 11:54:05 |
| 103 |  5368709120 | 2021-10-06 11:54:05 | ←スケールアップ完了（innodb_buffer_pool_size増加）
| 104 |  5368709120 | 2021-10-06 11:54:06 |
+-----+-------------+---------------------+
```
→**ダウンタイムなし!**ターミナル上もエラー表示はなくコンソールからの実施後5秒で完了。

### 2-core/8GB MEM -> 2-core/4GB MEMスケールダウン

```
+-----+-------------+---------------------+
| id  | buffer_pool | createtime          |
+-----+-------------+---------------------+
| 367 |  5368709120 | 2021-10-06 12:05:59 |
| 368 |  5368709120 | 2021-10-06 12:06:00 | ←コンソール上でスケールダウン実施
| 369 |  5368709120 | 2021-10-06 12:06:00 | 
| 370 |  5368709120 | 2021-10-06 12:06:01 |
| 371 |  5368709120 | 2021-10-06 12:06:01 |
| 372 |  5368709120 | 2021-10-06 12:06:02 |
| 373 |  5368709120 | 2021-10-06 12:06:02 |
| 374 |  5368709120 | 2021-10-06 12:06:03 |
| 375 |  5368709120 | 2021-10-06 12:06:03 |
| 376 |  5368709120 | 2021-10-06 12:06:04 |
| 377 |  2147483648 | 2021-10-06 12:06:04 | ←スケールダウン完了（innodb_buffer_pool_size減少）
| 378 |  2147483648 | 2021-10-06 12:06:05 |
+-----+-------------+---------------------+
```
→**ダウンタイムなし!**エラーもなし。

sleepを0.01(10msec)まで縮めてもエラー出力を補足する事は出来ませんでした。アプリケーション層からはほぼ無停止に見えると考えてよさそうです。

# まとめ
ダウンタイム順に以下の様な結果となりました。
**TDSQL-C < Aurora(with RDSProxy) <<< Aurora(w/o RDSProxy)**

公式見解通り、TDSQL-Cの超高速スケーリング性能はなかなか衝撃的な結果が得られました。しかもAuroraのように2ノードクラスタではなく、シングルノードでの結果というのも驚異的です。現時点でTDSQL-Cにオートスケール機能は提供されていませんが、モニタリングアラートをトリガーにAPI経由でスケーリングを走らせる事は出来るかもしれません（もっと良い方法があれば教えてください）

インフラ最適化の観点から「インサービスのまま1～1.5秒のダウンタイムを許容しつつスケーリングする」という選択肢はゲームインフラではまず支持されないと思いますが、アプリケーション層で無停止であれば一日の中で複数回スケーリングするような運用も可能になるかもしれません。マイネットでもまだまだ検証中ですが、ゲーム空間の長命化のためにも実現に向けて研究を進めていきたいと考えています。

と、いいながらも一般的には「TDSQL-Cが使えるからtencent cloudを採用しよう！」とはまだまだならないと思います。ですが、ゲームタイトルに最適なインフラを模索する上で選択肢の一つにはなり得るかもしれません。
また、無停止スケーリングのアプローチが注目を集め、市場に潜在的なニーズがある事が証明されればAuroraなど競合プロダクトでの対応も期待できるかもしれないですね。

さて、最後となりますが、株式会社マイネットでは一緒に働く仲間を募集しています！
弊社では様々なゲームタイトルをより長く、安定して運営していくために、インフラ最適化にも積極的に取り組んでいます。興味のある方、ご応募お待ちしております！
https://mynet.co.jp/recruitment/