PUSHCODEのDB移行&更新

この記事は[EDOCODE Advent Calendar 2023](https://adventar.org/calendars/8641)、12月21日(木)の記事です。

1つ前の記事は、Ameeさんの[The Power of Storytelling in E-commerce Marketing: Transforming Shopify Stores for Success](https://www.gojiberry.app/blog/the-power-of-storytelling-in-e-commerce-marketing-transforming-shopify-stores-for-success)でした。
また、グループ会社　Wanoの[Advent Calendar](https://qiita.com/advent-calendar/2023/wano-group)もありますので、そちらもどうぞ！

この記事では、今年の夏に行った[PUSHCODE](https://www.pushcode.jp/)のDB移行&更新プロジェクトに関して書きます。


## 移行内容と背景

このプロジェクトではDBの移行(GCP Compute EngineのCentOS上で動いていたMySQL -> Cloud SQL for MySQL)とMySQLのVersion更新(MySQL 5.7 -> MySQL 8.0)を同時に行いました。これに伴うサービスのダウンタイムは30分ほどでした。

DBの移行の背景はインフラに手を回すリソースがあまりないため、managedサービスに移行しました。またMySQLの更新は移行前に利用していたMySQL 5.7のEOFが今年の10月だったためです。他のDBMSも検討をしましたがpartitionに余裕があることもあり、移行前と同じMySQLになりました。


## 移行前と移行後の構成

移行前と移行後の構成は下記のようになっています。オレンジ色がCompute EngineのCentOS上で動いていたMySQL 5.7です。

![移行前と移行後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63677/e6432550-b424-0a58-e701-584b122407ef.png)


※MHA(=Master High Availability Manager and tools for MySQL)
　移行前に利用していたHigh Availabilityのための[ツール](https://github.com/takumakume/mha-rpm)

※HA(=High Availability)
　Cloud SQLでHigh Availabilityを実現する[設定](https://cloud.google.com/sql/docs/mysql/high-availability)です。これをonにすることで移行前と同程度の可用性を担保することができました。



## 移行の流れ

移行は以下のステップで行いました。今回説明するのは2と5の太字の箇所です。

1. 公式ドキュメントから更新情報を集める
    - 移行前に利用していたMHAを利用しながらDMSを利用できるかの確認
    - MySQL 5.7->8.0の変更点の確認
    - 移行前と同じavailabilityを担保するのに必要な設定の確認
    - Cloud SQLで使える設定の確認
3. **コード修正と設定変更**
2. テスト環境での検証&動作確認
4. 移行前準備(下の図の②)
    - 移行前DBと移行先DBをDMSを使ってreplication
5. **パフォーマンス計測　＆　移行先のspec決め**
6. 移行作業(下の図の③)
    - コード変更をリリース
    - DMSで移行先DBをpromote
7. 移行後の監視設定


移行の流れを図にしたのがこちらです。
![移行の流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63677/07763ff6-f5ff-0028-71eb-cb08540c25d8.png)


※DMS(= Database Migration Service)
　移行時のダウンタイムを少なくするために移行前のDBをreplicatationする必要がありました。その際に利用したGCPの[サービス](https://cloud.google.com/database-migration?hl=en)です。これを使うことで移行前にMHAを使いながら同時に移行先DBにreplicationを行うことが出来ました(図の②)。
 
## **コード修正と設定変更**

変更内容は一つ前のステップで行った[Upgradeのガイド](https://dev.mysql.com/doc/refman/8.0/en/upgrading.html),[Upgrade Checker Utility](https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-shell-utilities-upgrade.html),[Cloud SQLのflag](https://cloud.google.com/sql/docs/mysql/flags#list-flags-mysql)を参考にして一覧を作成しました。読むドキュメントの量が多いこと、またこのステップ後に動作確認を行いますが、見落としがあるとincidentに繋がるため神経を使いました。

### コード修正

PUSHCODEではORMを使っていないため、コードベース中にSQLがあります。そのため、MySQLのversionアップに伴うSQLの変更に対応する必要がありました。該当する変更は1つで[GROUP BY byでの暗黙のソートの廃止](https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html#:~:text=The%20deprecated%20ASC%20or%20DESC%20qualifiers%20for%20GROUP%20BY%20clauses%20are%20removed.%20Queries%20that%20previously%20relied%20on%20GROUP%20BY%20sorting%20may%20produce%20results%20that%20differ%20from%20previous%20MySQL%20versions.%20To%20produce%20a%20given%20sort%20order%2C%20provide%20an%20ORDER%20BY%20clause.)のみでした。こちらは今まで暗黙でGROUP BYで指定したカラムでソートがされていましたが、同じ結果を得るためにMySQL 8からはORDER BYをつける必要があります。この変更のため順番が影響する箇所を調査し該当するところにはORDER BYの追加をしました。

### 設定変更
Cloud SQLでは[flag](https://cloud.google.com/sql/docs/mysql/flags#list-flags-mysql)で設定を行います。今回、移行に伴って削除したもの、値が変わったもの、追加になったものに分けていくつか紹介します。

- 削除したもの
        移行前は`innodb_buffer_pool_size`を指定していましたが、Cloud SQLが[デフォルトで最大値を設定する](https://cloud.google.com/sql/docs/mysql/flags#tips-buffer-pool)ため明示的に設定するのをやめて削除しました。
            
- 値が変わったもの
        `max_heap_table_size`と`tmp_table_size`の値を256M -> 67Mに変更しました。当初は移行前と同じ256Mを設定する予定でしたが最大67Mまでしか設定をすることができませんでした。[Tips](https://cloud.google.com/sql/docs/mysql/flags#tips-heap:~:text=max_heap_table_size%2C%20tmp_table_size)にもあるようにこれらに大きな値を設定するとinstanceが再起動する危険があるため設定できる最大値をMemoryに応じて制限していると思われます。
      
- 追加したもの
        `general_log`と`default_time_zone`を追加しました。`general_log`は移行後にDBが期待通り動いているかの確認のため一時的にonにしました。また。Cloud SQLでは[デフォルトのtimezoneがUTC](https://cloud.google.com/mysql/cloudsql-setup#:~:text=the%20new%20instance.-,Review%20time_zone,-The%20time%20zone)のため`default_time_zone`を-9:00にする必要がありました。
            


## パフォーマンス計測　＆　移行先のspec決め

本番環境相当のデータを試験環境で用意する時間の余裕がなかったため、本番データをreplicationした後にパフォーマンス計測と移行先instanceのスペック決めを行いました。

### 計測用のクエリ

遅いクエリを探すため、[slow query](https://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html)を有効にして[mysqldumpslow](https://dev.mysql.com/doc/refman/8.0/en/mysqldumpslow.html)を使ってクエリのリストを参照しました。クエリのリストから多くのtableをjoinしている&partitionを切っていて多くのレコードが入っているものをパラメータとして選びました。
また、キャッシュによる影響を排除するためorder byにrand()を使ったクエリでも計測を行いました。EX: `select * from users order by rand() limit 100;`


### 移行先のspec決め

移行前と同等のCPU,MemoryのCloud SQLのinstanceで期待通りのパフォーマンスが出ませんでした(最大2.5倍実行に時間がかかった)。その後調査を行うと[Enterprise Plus](https://cloud.google.com/sql/docs/editions-intro)(Cloud SQL for MySQLのeditionの一つ)の存在を知り、CPU, Memory, Enterprise(defaultのedition) or Enterprise Plusを変えパフォーマンス計測を行いました。最終的にはEnterprise Plusで移行前のCPUの半分のコア数、memoryは6割の量で決まりました。

## 最後に

本番環境のDB移行をするのは今回が初めてでした。また普段の開発ではDBをあまり意識する機会が少なかったです。このプロジェクトを通してDBの学びや理解が深まる良い機会になりました。今後もキャッチアップをしつつDBのパフォーマンスを最大限に出せるようにスペックや設定の見直しを適宜行う予定です。
今回のプロジェクトを終えるにあたり、@ktatさん、@fujitayyさんに多くのヘルプをしていただきました。ありがとうございました。


明日12/22(金)は、デザイナーのTingyaさんによる「[他言語でのuser interview](https://medium.com/@bttty629/%E4%BB%96%E8%A8%80%E8%AA%9E%E3%81%A7%E3%81%AEuser-interview-389f6e04c7c0)」という記事です。

また、現在Wanoグループでは人材募集をしています。興味のある方は下記を参照してください
[JOBS | Wano Group](https://group.wano.co.jp/jobs/)