この記事は Fredric DescampsMigrate MyISAM tables from MySQL 5.7 to 8.0を日本語版として翻訳および再編集したものです。

MySQLは、以前からデフォルトのトランザクションエンジンとしてInnoDBを採用しています。もしまだMyISAMを使用している場合は、InnoDBに変換してそのベネフィットを感じていただきたいと思います。
MySQL 8.0にアップグレードする予定なら、MyISAMテーブルを処理するためのオプションがあります。

MyISAMテーブルの使用は推奨されないことに注意してください。MyISAMはACIDに準拠していないため、データの損失につながる可能性があります。
また、MyISAMは完全なテーブルロックのみをサポートし、行レベルのロック機能はありません。

5.7では、MyISAMはMYD、MYI、FRMファイルを保持していました。8.0では、FRMはSDI(Serialized Dictionnary Information)に置き換わっています。
MyISAMテーブルを8.0に移行するための方法について説明します。

移行方法

MyISAMデータを8.0に移行するには、複数の方法があります。

  1. インプレースアップグレード(mysqldを停止し、バイナリを置き換えてmysqldを再起動する)
  2. 論理的なダンプとロード(MySQL Shellを使用するのが便利です)
  3. 物理的なコピー(若干の手間がかかります)

方法2、3は、MySQL5.7と8.0が別サーバーにインストールされている場合に有効です。

移行後はInnoDBエンジンに変更することをお勧めします!

インプレースアップグレード

5.7から8.0への移行は、これが最も一般的な方法です。こちらのブログ記事を参考にしてください。

論理的なダンプとロード

MySQL 5.7 の MyISAM データソースと MySQL 8.0 の移行先が異なるサーバにある場合、データの論理的ダンプ&ロードは、一方のサーバからもう一方のサーバに移行するために良い方法です。

このようなダンプとロードを実行するには、MySQLシェルユーティリティが一番適切なツールです。

ここでは、MySQLシェルユーティリティに関連した記事を紹介します。

推奨されているInnoDBに移行する場合、データのロードは3ステップで行うことができます。

  1. テーブルの定義をロードする
  2. この空のテーブルのエンジンをInnoDBに変更する
  3. データをロードする

では、実際に見てみましょう。まずはダンプ取得から行います。

Server version: 5.7.31 MySQL Community Server (GPL)
No default schema selected; type \use <schema> to set one.
MySQL  127.0.0.1:5731  2021-08-02 12:10:15 
JS > util.dumpSchemas(['my_isam_db'],'/tmp/dump')
Acquiring global read lock
Global read lock acquired
Gathering information – done
All transactions have been started
Locking instance for backup
NOTE: Backup lock is not supported in MySQL 5.7 and DDL changes will not be blocked.
The dump may fail with an error or not be completely consistent 
if schema changes are made while dumping.
Global read lock has been released
Writing global DDL files
Preparing data dump for table `my_isam_db`.`t1`
Data dump for table `my_isam_db`.`t1` will be chunked using column `id`
Writing DDL for schema `my_isam_db`
Data dump for table `my_isam_db`.`t1` will be written to 1 file
1 thds dumping – 100% (2 rows / ~2 rows), 0.00 rows/s, 0.00 B/s uncompressed, 0.00 B/s compressed
Duration: 00:00:00s                                                                              
Schemas dumped: 1                                                                                
Tables dumped: 1                                                                                 
Uncompressed data size: 55 bytes                                                                 
Compressed data size: 51 bytes                                                                   
Compression ratio: 1.1                                                                           
Rows written: 2                                                                                  
Bytes written: 51 bytes                                                                          
Average uncompressed throughput: 55.00 B/s                                                       
Average compressed throughput: 51.00 B/s

複数のスキーマ、またはインスタンス全体をダンプすることができます。
ここで、MySQL Shell を MySQL 8.0 に接続し、テーブル定義のみをロードしてみます。

JS>  util.loadDump('/tmp/dump', {'ignoreVersion': true, 'loadData': false})
Loading DDL only from '/tmp/dump' using 4 threads.
Opening dump…
Target is MySQL 8.0.26. Dump was produced from MySQL 5.7.31
WARNING: Destination MySQL version is newer than the one where the dump was created. 
Loading dumps from different major MySQL versions is not fully supported and may not work. 
The 'ignoreVersion' option is enabled, so loading anyway.
Checking for pre-existing objects…
Executing common preamble SQL
Executing DDL script for schema `my_isam_db`
[Worker002] Executing DDL script for `my_isam_db`.`t1`
Executing common postamble SQL                      
                                                    
No data loaded.
0 warnings were reported during the load.

loadData:falseを使用していることがおわかりいただけると思います。
ここで、エンジンをInnoDBに変更します。

SQL> show create table t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `inserted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
1 row in set (0.0005 sec)
SQL> alter table t1 engine=innodb default charset=utf8mb4;
Query OK, 0 rows affected (0.3546 sec)
Records: 0  Duplicates: 0  Warnings: 0
SQL> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET latin1 DEFAULT NULL,
  `inserted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.0012 sec)
 

また、このタイミングでキャラクタセットをUFT8MB4に変更したこともお分かりいただけると思います。
ここまででデータをロードする準備ができました。

JS> util.loadDump('/tmp/dump', {'ignoreVersion': true, 'loadDDl': false})
Loading Data only from '/tmp/dump' using 4 threads.
Opening dump…
Target is MySQL 8.0.26. Dump was produced from MySQL 5.7.31
WARNING: Destination MySQL version is newer than the one where the dump was created. 
Loading dumps from different major MySQL versions is not fully supported and may not work. 
The 'ignoreVersion' option is enabled, so loading anyway.
NOTE: Load progress file detected. Load will be resumed from where it was left, assuming no external updates were made.
You may enable the 'resetProgress' option to discard progress for this MySQL instance and force it to be completely reloaded.
Executing common preamble SQL
[Worker000] my_isam_db@t1@@0.tsv.zst: Records: 2  Deleted: 0  Skipped: 0  Warnings: 0
Executing common postamble SQL                                       
                                                       
1 chunks (2 rows, 55 bytes) for 1 tables in 1 schemas were loaded in 1 sec (avg throughput 55.00 B/s)
0 warnings were reported during the load.

完了しました!
 

物理的なコピー

データファイル(.MYDと.MYI)を元のサーバーから移行先のサーバーにコピーしたい方もいらっしゃると思います。
ただし、SDIが欠落しテーブルが認識されないため、実行することができません。スキーマを作成するまでは、データベースとテーブルが無視されるだけで、エラーは発生しません。

mysql> create database my_isam_db;
ERROR 3678 (HY000): Schema directory './my_isam_db' already exists. 
This must be resolved manually (e.g. by moving the schema directory to another location).

では、どのような方法があるのでしょうか?
最も安全な方法は、テーブル定義のみのダンプを作成することです。(前のセクションと同じダンプファイルを使用することもできますが、非常に大きなデータをダンプすることを避けるためにファイルコピーを選択するのかもしれません)
では、テーブル定義だけを含むダンプを取得してみましょう。

JS> util.dumpSchemas(['my_isam_db'],'/tmp/dump_ddl', {'ddlOnly': true})
Acquiring global read lock
Global read lock acquired
Gathering information – done
All transactions have been started
Locking instance for backup
NOTE: Backup lock is not supported in MySQL 5.7 and DDL changes will not be blocked. 
The dump may fail with an error or not be completely consistent if schema changes are made while dumping.
Global read lock has been released
Writing global DDL files
Running data dump using 4 threads.
NOTE: Progress information uses estimated values and may not be accurate.
Writing DDL for table `my_isam_db`.`t1`
Writing DDL for schema `my_isam_db`
0% (0 rows / ~2 rows), 0.00 rows/s, 0.00 B/s uncompressed, 0.00 B/s compressed
Duration: 00:00:00s                                                           
Schemas dumped: 1                                                             
Tables dumped: 1                                                              
Uncompressed data size: 0 bytes                                               
Compressed data size: 0 bytes                                                 
Compression ratio: 0.0                                                        
Rows written: 0                                                               
Bytes written: 0 bytes                                                        
Average uncompressed throughput: 0.00 B/s                                     
Average compressed throughput: 0.00 B/s

これで、取得したダンプをロードすることができます。ロードすると、.FRMファイルはなく、.SDIが存在する空のMyISAMテーブルが作成されます。

JS> util.loadDump('/tmp/dump_ddl', {'ignoreVersion': true})
Loading DDL and Data from '/tmp/dump_ddl' using 4 threads.
Opening dump…
Target is MySQL 8.0.26. Dump was produced from MySQL 5.7.31
WARNING: Destination MySQL version is newer than the one where the dump was created. 
Loading dumps from different major MySQL versions is not fully supported and may not work. The 'ignoreVersion' option is enabled, so loading anyway.
Checking for pre-existing objects…
Executing common preamble SQL
Executing DDL script for schema `my_isam_db`
[Worker000] Executing DDL script for `my_isam_db`.`t1`
Executing common postamble SQL               
                                             
No data loaded.
0 warnings were reported during the load.

SQLコマンドでテーブルが作成されたことが確認できます。

SQL> show create table t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `inserted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
1 row in set (0.0003 sec)

ファイルシステムでも確認することができます。

total 8.0K
-rw-r—– 1 mysql mysql 3.6K Aug  2 12:49 t1_5017.sdi
-rw-r—– 1 mysql mysql    0 Aug  2 12:49 t1.MYD
-rw-r—– 1 mysql mysql 1.0K Aug  2 12:49 t1.MYI

あとは、MYDとMYIのファイルを正しい場所にコピーするだけです。ただし、その前にMySQLを停止する必要があります。

[root@imac mysql]# systemctl stop mysqld
[root@imac mysql]# cp data/my_isam_db/t1.M* /var/lib/mysql/my_isam_db/
cp: overwrite '/var/lib/mysql/my_isam_db/t1.MYD'? y
cp: overwrite '/var/lib/mysql/my_isam_db/t1.MYI'? y

ファイルのコピー後にMySQLを再起動します。

追記

時にはMySQLサーバはもう運用されていないが、データはどこか(バックアップやディスクなど)にあるということがあります。
スキーマとテーブル定義のダンプを実行できない場合、FRMファイルがあれば、テーブル定義を提供する外部ツール:dbsakeが利用できる可能性があります。

dbsakeは、frmファイルを読むことができるだけでなく、fincoreの機能に関しても特に興味深いツールです。
では、これらのファイルがある古いMySQL 5.7ディレクトリにしかアクセスできないと仮定してみましょう。

[root@imac data]# ls -lh my_isam_db/
total 24K
-rw-r—– 1 fred fred   65 Jul 29 08:45 db.opt
-rw-r—– 1 fred fred 8.5K Jul 29 09:29 t1.frm
-rw-r—– 1 fred fred   40 Jul 29 09:29 t1.MYD
-rw-r—– 1 fred fred 2.0K Jul 29 09:29 t1.MYI

そのfrmファイルを使ってdbsakeを使えば、テーブルの定義を取得することができます。

[root@imac data]# dbsake frmdump my_isam_db/t1.frm 

— Table structure for table `t1`
— Created with MySQL Version 5.7.31

CREATE TABLE `t1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL,
  `inserted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

上記はMySQL 8.0に移行する必要があるすべてのMyISAMテーブルに対して行うことができます。

結論

このように、古いバージョンのMySQLから8.0にMyISAMデータを移行することは可能です。
しかし、最も重要なことはより高性能で安全なエンジンであるInnoDBに変更することです!