MySQL5.7から8.0に移行

# はじめに
RDSのMySQL5.7がサポート終了に伴い、MySQL8.0へのバージョンアップが必要になりました。
その際に調べたことについて書いていこうと思います。

# 環境
- 仮装環境
    - Docker
- 言語＆フレームワーク
    - PHP8系
    - Laravel10
- DB
    - MySQL5.7
    - MySQL8.0
# アップグレード時の注意点
### 1. デフォルト文字コードの変更

MySQL8.0から、デフォルトの文字コードが**utf8mb4**になった。

絵文字や幾つかの特殊な文字を保存できるようになった反面、今まで使用していた文字コードと変わってしまう。

加えて、defaultのcharacter setで設定していた人は、 今まで使っていた文字コードと変わってしまうので、自分で以前使っていた文字コードを指定する必要がある。

### 2. **予約語の追加**

MySQL8.0で幾つか予約語が追加された。

それにより今までのSQL文やカラム名で使用できていた単語が予約語に引っかかり構文エラーなどが生じる。

- CUBE
- EMPTY
- JSON_TABLE
- LAST_VALUE
- RANK
etc

詳しくは「[公式サイト](https://dev.mysql.com/doc/refman/8.0/ja/keywords.html)」を参照

### 3. **変数、関数の削除と追加**

クエリの中で使える変数や関数にも変更があった。

削除されたものを使用している場合、実行されなくなる。

- 変数（廃止）
    - **query_cache_size, query_cache_type**
        - クエリキャッシュはMySQL 8.0で完全に削除された
    - **secure_auth**
        - この変数は廃止され、MySQL 8.0以降ではセキュアなパスワード認証は常に必須となった
    - **old_passwords**
        - MySQLの旧バージョンのパスワードハッシュ方式に関連していたが、MySQL 8.0では削除された
- 関数（廃止）
    - **PASSWORD()**
        - ユーザーパスワードのハッシュを生成するために使われていたが、廃止された
    - **ENCRYPT()**
        - この関数は暗号化のためのものだったが、他のより安全な関数やアルゴリズムに置き換えられた
    - **DES_ENCRYPT(), DES_DECRYPT()**
        - これらの関数も同様に暗号化のためのものだったが、MySQL 8.0で削除された
- 関数（追加）
    - **JSON_ARRAYAGG()**
        - JSON配列として集約された結果セットの値を返す
    - **JSON_OBJECTAGG()**
        - キーと値のペアを使用して、集約された結果セットからJSONオブジェクトを作成する
    - **JSON_TABLE()**
        - JSONドキュメントを関係データの行と列に変換、通常のテーブルのようにJOINやクエリを実行できる
    - **JSON_OVERLAPS()**
        - 2つのJSONドキュメントが共通の要素やキーを持っているかどうかを判定する
    - **JSON_MERGE_PATCH()**
        - 2つのJSONドキュメントを統合するが、 `JSON_MERGE_PRESERVE()`とは異なり、2つ目のオブジェクトでNULLの値を持つキーは最初のオブジェクトから削除される
    - **JSON_STORAGE_SIZE()**
        - JSONドキュメントがディスク上で消費するバイト数を返す
    - **JSON_STORAGE_FREE()**
        - JSONドキュメント内で未使用のスペースのバイト数を返す
    - **JSON_KEYS()**
        - JSONオブジェクトのキーの配列を返す
etc

詳しくは「[公式サイト](https://dev.mysql.com/doc/refman/8.0/ja/added-deprecated-removed.html)」を参照

### 4. **デフォルトの認証プラグインの変更**
MySQL8.0からデフォルトの認証プラグインとして`caching_sha2_password`が導入された。

旧バージョンの`mysql_native_password`よりもセキュリティが向上している。

これにより、アプリケーションやライブラリが新しい認証プラグインをサポートしているかどうかを確認し、必要に応じて更新や設定変更を行う。

# アップグレード手順
1. docker-compose.ymlの修正
    **Laravel以外（SQLで直接）から新規でテーブルとカラムを追加する可能性がない場合**
    ```diff_yaml
    services:
      db:
    -    image: mysql:5.7
    +    image: mysql:8.0
    ```
    **Laravel以外（SQLで直接）から新規でテーブルとカラムを追加する可能性がある場合**
    my.cnfファイルの作成
    下記の内容を記載した`my.cnf`ファイルをdockerディレクトリへ格納する。
    ```text
    [mysqld]
    character-set-server=utf8mb4
    collation-server=utf8mb4_unicode_ci
    ```
    
    docker-compose.ymlの修正
    ```diff_yaml
    // 既存
    services:
      db:
    -    image: mysql:5.7
    +    image: mysql:8.0
    +    volumes:
    +          - ./_docker/my.cnf:/etc/mysql/my.cnf
    ```
　
2. コンテナ再作成
`docker-compose down`で既存のコンテナをストップさせてから`docker-compose up -d`の実行を行う。
　
3. テスト実行
問題なく動くか確認する。

# 検証時に行った手順
アプリケーション側の自動テストが充実してたこともあり、**アップグレード時の注意点**を頭に入れつつとりあえず、アップグレードすることにしました。
1. docker-compose.ymlの修正
    ```diff_yaml
    services:
      db:
    -    image: mysql:5.7
    +    image: mysql:8.0
    ```
　
2. `docker-compose up -d`の実行
既存のコンテナを削除して`docker-compose up -d`の実行を行う。
　
3. マイグレーションの実行
現在の環境がPHP8系、Laravel10ということもあり、`caching_sha2_password`認証プラグインに対応している。（**PHP7.4以降**から対応されている）

:::note warn
もしも古いバージョンの場合はこの段階でエラー発生します。
:::
その場合、前項にも挙げた「**デフォルトの認証プラグインの変更**」の影響があるので、バージョンアップの前に下記の手順を行う。

3-1. MySQL5.7のdefault-authentication-pluginを確認

![5.7(認証).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690391/692f7b8d-ca87-bedb-aa79-b498feb43202.png)
*MySQL5.7*

何も行わず、MySQL8.0へバージョンを上げると
![8(認証).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690391/e8f4db23-257e-9c3e-6199-0c9022f88301.png)
*MySQL8.0*

3-2. my.cnfを追加とdocker-compose.ymlの修正
`my.cnf`とは、MySQLの設定ファイルで、パラメータの設定などをする際に用いられる。
詳しくは「[公式サイト](https://dev.mysql.com/doc/refman/8.0/ja/option-files.html)」を参照
　
下記の内容を記載した`my.cnf`ファイルをdockerディレクトリへ格納する。
```text
[mysqld]
default-authentication-plugin = mysql_native_password
```
その後、docker-compose.ymlのvolumesにmy.cnfファイルを指定する。
```diff_yaml
services:
  db:
    image: mysql:8.0
+	volumes:
+      - ./my.cnf:/etc/mysql/my.cnf
```

3-3. バージョンアップ処理
マイグレーションまで問題なく実行される。
`default-authentication-plugin`が変更されていることも確認
![5.7(認証) (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690391/aece9f4e-45fc-49c8-aa19-e4fde280a156.png)
*MySQL8.0*
　
4. テストの実行
　テスト成功
　
5. MySQLの文字コードを確認
　テストは成功したが、バージョンアップの注意点にもあった文字コードを確認する。
 ```sql
 SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
	FROM information_schema.SCHEMATA
	WHERE SCHEMA_NAME = 'データベース名';
```
![5.7_文字コード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690391/227991a4-8f2e-63b8-2507-c548fb202f66.png)
*MySQL5.7*

![8_文字コード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690391/30726b94-d1b8-0f47-c4c8-9f3e96b8438e.png)
*MySQL8.0*

DEFAULT_CHARACTER_SET_NAMEとDEFAULT_COLLATION_NAMEが変更されてしまっている。

これが変更されることにより、新規テーブル、新規カラム作成時にこちらの文字コードがデフォルトへ設定されてしまう。

しかし、Laravelの[config/database.php]に文字コードを設定していた場合、以降の対応は不要

```php
<?php

return [
    'connections' => [
        'mysql' => [
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
        ],
    ],
];
```
※マイグレーションから操作する場合は問題ないが、MySQLを直接操作して、新規テーブル、新規カラム作成する場合、バージョンアップ前に以降の設定が必要

5-1. my.cnfを追加とdocker-compose.ymlの修正
下記の内容を記載した`my.cnf`ファイルをdockerディレクトリへ格納する。

```text
# 既存のLaravelと同じにしたい場合
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

# MySQL5.7と同じ設定にしたい場合
[mysqld]
character-set-server = latin1
collation-server = latin1_swedish_ci
```

その後、`docker-compose.yml`のvolumesにコピー先を指定する。

```yml
services:
  db:
    image: mysql:8.0
	volumes:
      - ./my.cnf:/etc/mysql/my.cnf
```

5-2. アップグレード処理
テストまで終了し、文字コードを確認する
```sql
SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
	FROM information_schema.SCHEMATA
	WHERE SCHEMA_NAME = 'データベース名';
```
![8_文字コード(対応後_新).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690391/e3b56816-a425-37e5-146a-fec881284978.png)
*MySQL8.0(対応後、既存のLaravelと同じにしたい場合)*
![8_文字コード(対応後).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690391/7bf30f75-70f6-89cc-c3f4-38eb8c7cf934.png)
*MySQL8.0(対応後、MySQL5.7と同じ設定にしたい場合)*

# 参考サイト

https://baapuro.com/database/eleven/

https://qiita.com/zackey2/items/e7fa42107aaee71f7d3c

https://labs.gree.jp/blog/2023/07/22581/

https://techblog.hacomono.jp/entry/2023/12/20/070000