GA

2024/12/24

gh-ostの-postpone-cut-over-flag-fileみたいなことをpt-oscでもやりたい

この記事は MySQL Advent Calendar 2024 の24日目の記事です。

昨日は updraftさん今日は、MySQL 8.0.35で非推奨になった「SHOW PROCESSLIST」の代わりのパフォーマンススキーマを見てみるの日。 - 今日はなにの日。 でした。


最近、ALTER TABLEのメタデータロックの競合がちょっと話題に上がっていました(ので便乗して今年書いたスライドを載せておきます)

それでちょっと思い出したんですが、 gh-ostpt-online-schema-change (以下、pt-osc) とメタデータロックを比較するとこんな感じになります。

type 開始時排他MDL 終了時排他MDL
ネイティブALTER TABLE(ALGORITHM=INPLACE) 取る 取る
ネイティブALTER TABLE(ALGORITHM=INSTANT) 取る 取らない
gh-ost 取らない 取る
pt-osc 3回取る 取る

pt-oscの仕組み的にトリガーを使うので、 CREATE TRIGGER のぶんだけ (INSERT Trigger, Update Trigger, Delete Triggerがそれぞれ同時に作れないので計3回) 排他MDLが必要になります。

で、ネイティブALTER TABLEにせよgh-ost, pt-oscにせよ

があって、開始時のMDLは「まあCtrl+Cですぐ中断できるし lock_wait_timeout でAbortさせても良い」んですが終了時のMDLは「終わる時間が完全には読みにくい」とか「終わる時間が深夜になると無理」とか「ここでタイムアウトしちゃうと作業が最初からやり直し」とかがあります。

gh-ostは(おそらく作ってる人たちがそもそもこの悩みを抱えていて) -postpone-cut-over-flag-file というのがあって、この「終了処理で排他MDLを取らないといけないステップをファイルの存在で遅延させる」仕組みがあります。

たとえば -postpone-cut-over-flag-file /tmp/flag とかやっておくと、 /tmp/flag がある間は終了処理に入らない(gh-ostを起動した時点でファイルは勝手に作られる)ので、ピークタイムを避けてゆっくり余裕がありそうな時間に rm /tmp/flag で排他MDLの処理を実行させることができます。

対して、pt-oscにはデフォルトでこの機能はない ( —pause-file だとカットオーバー以外の INSERT IGNORE INTO .. の部分も止まってしまう) ので、自分で --plugin で使える Perlスクリプト を書いてフックしてやる必要があります。

pt-oscのドキュメントにも書いてあるんですがこの形の方が人に説明しやすかったので前に書いたものがこちらです。

https://github.com/yoku0825/pt-osc-plugin/blob/main/pt-osc-plugin.pl

init(pt-osc起動時に呼ばれるフック)でフラグファイルを作ってあげて

sub init
{
  my ($self, %args)= @_;
  _logger(DEBUG, "start init");
  open(my $flag_file, ">", "/tmp/flag");
  close($flag_file);
  _logger(DEBUG, "finish init");
}

before_swap_tables(排他MDLが必要になる処理の直前)でフラグファイルをチェックしてあげれば

sub before_swap_tables
{
  my ($self, %args)= @_;
  _logger(DEBUG, "start before_swap_tables");

  while (-e "/tmp/flag")
  {
    _logger(INFO, "flag file does not exist, sleepling..");
    sleep 5;
  }

  _logger(DEBUG, "finish before_swap_tables");
}

↓pt-oscの終了(するときの排他MDL)のタイミングを調整できます。

$ PLUGIN_LOG_LEVEL=9 pt-online-schema-change --user=root --socket=/usr/mysql/8.0.40/data/mysql.sock --alter "Engine = InnoDB" D=d1
,t=t1 --execute --plugin=/tmp/pt-osc.pl

..
start after_copy_rows at pt_online_schema_change::main
finish after_copy_rows at pt_online_schema_change::main
start before_swap_tables at pt_online_schema_change::main
flag file does not exist, sleepling.. at pt_online_schema_change::main
flag file does not exist, sleepling.. at pt_online_schema_change::main
flag file does not exist, sleepling.. at pt_online_schema_change::main

「pt-oscは終了時の排他MDLが制御できなくて…」という時に試してみてください。

明日は zoosm3さん です!

2024/12/20

Blackholeストレージエンジンとバイナリログと 2024

この記事は MySQL Advent Calendar 2024 の20日目の記事です。
昨日は asahideさんGoogle Cloudでも使えるHeatWave MySQL でした。


TL;DR

  • binlog_format=ROWlog_replica_updates=ON (MySQL 8.0とそれ以降のデフォルトのまま)
  • ソースが BLACKHOLEストレージエンジン の場合は INSERT はバイナリログに載るが UPDATE/DELETE はバイナリログに載らない
    • これは知ってた
  • ソースがInnoDBでレプリカがBLACKHOLEの場合、ソースで成立した INSERT だけでなく UPDATE/DELETE もレプリカのバイナリログに吐く
    • これうまく使えばbinlog_serverになるんじゃ?

まだ MySQL::Sandbox を使っている。

$ make_replication_sandbox --how_many_slaves=1 8.0.40
$ cd sandboxes/rsandbox_8_0_40/
$ ./m

CREATE TABLE d1.innodb (num int) Engine = InnoDB;
CREATE TABLE d1.myisam (num int) Engine = MyISAM;
CREATE TABLE d1.blackhole (num int) Engine = BLACKHOLE;

FLUSH BINARY LOGS;  -- 見やすくするため

INSERT INTO d1.innodb VALUES(1);
INSERT INTO d1.myisam VALUES(1);
INSERT INTO d1.blackhole VALUES(1);

UPDATE d1.innodb SET num = 2 WHERE num = 1;
UPDATE d1.myisam SET num = 2 WHERE num = 1;
UPDATE d1.blackhole SET num = 2 WHERE num = 1;

master [localhost] {msandbox} ((none)) > SHOW WARNINGS;
+---------+------+--------------------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                            |
+---------+------+--------------------------------------------------------------------------------------------------------------------+
| Warning | 1870 | Row events are not logged for UPDATE statements that modify BLACKHOLE tables in row format. Table(s): 'blackhole.' |
+---------+------+--------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

DELETE FROM d1.innodb;
DELETE FROM d1.myisam;
DELETE FROM d1.blackhole;

master [localhost] {msandbox} ((none)) > SHOW WARNINGS;
+---------+------+--------------------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                            |
+---------+------+--------------------------------------------------------------------------------------------------------------------+
| Warning | 1870 | Row events are not logged for DELETE statements that modify BLACKHOLE tables in row format. Table(s): 'blackhole.' |
+---------+------+--------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

$ mysqlbinlog -vv master/data/mysql-bin.000002  | grep '^###'
### INSERT INTO `d1`.`innodb`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `d1`.`myisam`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `d1`.`blackhole`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### UPDATE `d1`.`innodb`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
### UPDATE `d1`.`myisam`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
### DELETE FROM `d1`.`innodb`
### WHERE
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
### DELETE FROM `d1`.`myisam`
### WHERE
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */

SHOW WARNINGS でも出てくる通り、BLACKHOLEストレージエンジンへの UPDATE/DELETE は無視されてバイナリログに載らない。
ソースのバイナリログに載らないから当然レプリカのバイナリログにも載らない。

$ mysqlbinlog -vv node1/data/mysql-bin.000001 | grep '^###'
### INSERT INTO `d1`.`innodb`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `d1`.`myisam`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### INSERT INTO `d1`.`blackhole`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### UPDATE `d1`.`innodb`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
### UPDATE `d1`.`myisam`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
### DELETE FROM `d1`.`innodb`
### WHERE
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
### DELETE FROM `d1`.`myisam`
### WHERE
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */

仮にソースでBLACKHOLE, レプリカでInnoDBなんてことを今日日(昔は binlog_format=STATEMENTMIXED なこともあったので UPDATE/DELETE もソースで空振りしてもバイナリログに落ちてレプリカに伝わる)やろうとすると、レプリカ側ではINSERTはされ続けるけどUPDATEやDELETEは常に伝わらないので無限に肥大化することになる。

$ ./m
CREATE TABLE d1.innodb_to_blackhole (num int) Engine = InnoDB;
FLUSH BINARY LOGS;

$ ./s
ALTER TABLE d1.innodb_to_blackhole Engine = BLACKHOLE;
FLUSH BINARY LOGS;

ところで、ソースでInnoDBにしてからレプリカをBLACKHOLEにする(昔のバッドノウハウの更に逆パターン)をすると(本当にやるなら SET SESSION sql_log_bin=OFF 推奨)

$ ./m

INSERT INTO d1.innodb_to_blackhole VALUES (1);
UPDATE d1.innodb_to_blackhole SET num = 2 WHERE num = 1;
DELETE FROM d1.innodb_to_blackhole;

$ mysqlbinlog -vv master/data/mysql-bin.000003 | grep '^###'
### INSERT INTO `d1`.`innodb_to_blackhole`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### UPDATE `d1`.`innodb_to_blackhole`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
### DELETE FROM `d1`.`innodb_to_blackhole`
### WHERE
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */

当然ソース(InnoDB)のバイナリログにも載るし

$ mysqlbinlog -vv node1/data/mysql-bin.000002 | grep '^###'
### INSERT INTO `d1`.`innodb_to_blackhole`
### SET
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### UPDATE `d1`.`innodb_to_blackhole`
### WHERE
###   @1=1 /* INT meta=0 nullable=1 is_null=0 */
### SET
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */
### DELETE FROM `d1`.`innodb_to_blackhole`
### WHERE
###   @1=2 /* INT meta=0 nullable=1 is_null=0 */

( д ) ゚ ゚ レプリカのバイナリログに載ってるー!

これを上手く使えれば、

  • mysqldだけどそんなにメモリもDiskも積まないマシンを作って
  • でもmysqldだからsemisyncでACKを返せて
  • リアルタイムでバイナリログだけを保管できて
  • 何なら CHANGE REPLICATION SOURCE TOsource_host に指定してバイナリログだけ引っ張ってこられる ( gtid_mode=ON がいいと思う)
    ようなbinlogサーバーが作れると思いませんか!!

たぶん試します。

明日は discus_hamburgさん です!

2024/12/16

MySQLのログと改行文字と個人の感想

この記事は MySQL Advent Calendar 2024 の16日目の記事です。
昨日の記事はまだ公開されていなさそうです。


TL;DR

  • MySQLのスローログ, ジェネラルログ, オプションによってはバイナリログも「実行されたクエリの改行文字まで全部記録する」
    • ゆえに改行文字を挟まずに投げてもらった方がgreppabilityが良くてうれしい
    • tail -f で見たい時以外は pt-query-digest を通せばどうにでもなる
  • 同じく「コメントも全て記録する」

TL;DRが全部言い切っちゃってますが、たとえばこんなSQLは

mysql80 19> INSERT INTO t1
    -> VALUES (100000001, 'one'),
    ->    (100000002, 'two');
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

こういう風に記録されます。

### スローログ
# Time: 2024-12-16T18:23:52.357891+09:00
# User@Host: root[root] @ localhost []  Id:    19
# Query_time: 0.013943  Lock_time: 0.000021 Rows_sent: 0  Rows_examined: 0 Thread_id: 19 Errno: 0 Killed: 0 Bytes_received: 70 Bytes_sent: 58 Read_first: 0 Read_last: 0 Read_key: 17 Read_next: 11 Read_prev: 0 Read_rnd: 0 Read_rnd_next: 0 Sort_merge_passes: 0 Sort_range_count: 0 Sort_rows: 0 Sort_scan_count: 0 Created_tmp_disk_tables: 0 Created_tmp_tables: 0 Start: 2024-12-16T18:23:52.343948+09:00 End: 2024-12-16T18:23:52.357891+09:00
SET timestamp=1734341032;
INSERT INTO t1
VALUES (100000001, 'one'),
   (100000002, 'two');

### ジェネラルログ
2024-12-16T18:23:52.344212+09:00           19 Query     INSERT INTO t1
VALUES (100000001, 'one'),
   (100000002, 'two')

### バイナリログ
# at 529
#241216 18:23:52 server id 1080  end_log_pos 529 CRC32 0x898f1fcf       Rows_query
# INSERT INTO t1
# VALUES (100000001, 'one'),
#    (100000002, 'two')
# at 529
#241216 18:23:52 server id 1080  end_log_pos 529 CRC32 0x5aec8a28       Table_map: `d1`.`t1` mapped to number 99
# has_generated_invisible_primary_key=0
# at 529
#241216 18:23:52 server id 1080  end_log_pos 529 CRC32 0xaf62b85b       Write_rows: table id 99 flags: STMT_END_F

BINLOG '
qPFfZx04BAAAVwAAAAAAAACAAD9JTlNFUlQgSU5UTyB0MQpWQUxVRVMgKDEwMDAwMDAwMSwgJ29u
ZScpLAogICAoMTAwMDAwMDAyLCAndHdvJynPH4+J
qPFfZxM4BAAANgAAAAAAAAAAAGMAAAAAAAEAAmQxAAJ0MQACCA8CgAACAQGAAgP8/wAoiuxa
qPFfZx44BAAAPQAAAAAAAAAAAGMAAAAAAAEAAgAC/wAB4fUFAAAAAANvbmUAAuH1BQAAAAADdHdv
W7hirw==
'/*!*/;
### INSERT INTO `d1`.`t1`
### SET
###   @1=100000001 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='one' /* VARSTRING(128) meta=128 nullable=1 is_null=0 */
### INSERT INTO `d1`.`t1`
### SET
###   @1=100000002 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='two' /* VARSTRING(128) meta=128 nullable=1 is_null=0 */

バイナリログは binlog_format がSTATEMENTの場合はもちろん改行込みで記録される。ROWの場合は実データ部分( Write_rows イベント) は影響を受けないけれど、 binlog_rows_query_events=ON で追加出力される Rows_query イベント (↑の出力の1つ目。2つ目のイベントは Write_rows ) は影響を受ける。

で、これが1行にまとまるとgreppabilityが上がって嬉しいという話です。主には tail -f で効きます。

$ tail -f general_log_or_slow_log | grep -i 'SELECT' | grep -i 'table_which_I_need_to_watch'
### SELECTとFROMが別の行にあるとgrepできない!

別に grep -B 2 とかで引けるといえば引けるんですが、そうするとSELECT, UPDATE, INSERT, DELETEを分けるのが若干面倒。
なお、 tail -f じゃなくていい/binlog以外なら単に pt-query-digest に食わせればいいだけです。

コードの中でSQLを書くときに改行コードを入れないと長いSQLは当然読みにくくなるので、クエリを実行する側で吸収すると便利なはず。

https://github.com/yoku0825/ytkit/blob/bd23f9c8ed0768829c597983bed5a223a677bdd2/lib/Ytkit/MySQLServer.pm#L521-L527

ついでに呼び出し元関数をコメントで添えるようにしておくといろいろ捗りました( ytkit の文字列と呼び出し元関数を両方入れておくことで、 grep -v ytkit だけで全部を除外できたりデバッグの時にジェネラルログから関数ごとに当たれたり)

この「呼び出し元関数をコメントで添える」アイディアは kazeburoさんのDBIx::Sunny を真似しました。
(いつぞやのISUCONでPerl参考実装にこれが使われていて感動したのが最初でした)

他、記憶は曖昧ですが DBeaver? とか Redashもコメントで呼び出した人(?) の情報を足してくれた気がします。
アプリケーションコードにコメントが埋め込まれていると、「今出てるスローログってどんなのです?」「xxxxx ってコメントがついたクエリなんですけど…」とかコミュニケーションが捗ります。

観測範囲でやってる人が大多数でないということはなんか難しいということなのかも知れませんが、やっておくといつか誰かを助けるかもしれないTIPSでした。

明日は wtnbnさん です!

2024/12/05

とあるDBAのMySQL サンドボックス

この記事は MySQL Advent Calendar 2024 の5日目の記事です。

昨日は yoku0825さんConoHa VPSでMySQLをビルドする 2024年 でした


昨日の記事では新しくRocky Linux 9でMySQLをビルドするために新調したインスタンスでやっていましたが、普段使いのインスタンスはEOLを迎えたCentOS7です

[ConoHa VPS](https://www.conoha.jp/vps/) の1GB(メモリ1GB, CPU2コア, SSD100GB)のやつです。これで3か月に1回くらいMySQLの新しいのがリリースされるたびにのんびりビルドしています。

バージョン間の動作確認やパッとコードを読むための環境として使っているので、MySQLはこんな風になっています。


$ ll /usr/mysql/* -d

drwxr-xr-x 10 yoku0825 yoku0825 4096 Jun 18  2021 /usr/mysql/5.0.96

drwxr-xr-x 11 yoku0825 yoku0825 4096 May  2  2021 /usr/mysql/5.1.73

drwxr-xr-x 13 yoku0825 yoku0825 4096 May  1  2024 /usr/mysql/5.5.62

drwxr-xr-x 13 yoku0825 yoku0825 4096 Sep 15  2021 /usr/mysql/5.6.51

drwxr-xr-x 11 yoku0825 yoku0825 4096 Jun  4  2024 /usr/mysql/5.7.44

drwxrwxr-x 13 yoku0825 yoku0825 4096 Dec  4 18:27 /usr/mysql/8.0.40

drwxrwxr-x 13 yoku0825 yoku0825 4096 Oct 17 09:45 /usr/mysql/8.4.3

drwxrwxr-x 13 yoku0825 yoku0825 4096 Oct 17 17:47 /usr/mysql/9.1.0

あと、MySQL Shellの dba.deploySandboxInstance が素直にPATH環境変数でmysqldを探してくれないので、それ用にrpmパッケージもインストールしてあります。

$ rpm -qa | grep mysql-community
mysql-community-icu-data-files-8.4.3-1.el7.x86_64
mysql-community-client-plugins-8.4.2-1.el7.x86_64
mysql-community-common-8.4.3-1.el7.x86_64
mysql-community-client-8.4.2-1.el7.x86_64
mysql-community-devel-8.4.2-1.el7.x86_64
mysql-community-libs-8.4.2-1.el7.x86_64
mysql-community-libs-compat-8.4.2-1.el7.x86_64
mysql-community-server-8.4.3-1.el7.x86_64

散らかっていて恐縮ですが、ソースコードはホームディレクトリに置いてあります。
ホームディレクトリで展開してインソースビルドしてmake installしているので、今 /usr/mysql にあるやつだけがディレクトリになっています。
容量の問題で、make installしたあとはmake cleanしてます。

$ ll -d mysql*
-rw-r--r--  1 yoku0825 yoku0825  11478592 Feb  6  2012 mysql-4.0.30.tar.gz
-rw-r--r--  1 yoku0825 yoku0825  22686667 Mar  2  2012 mysql-5.0.96.tar.gz
-rw-r--r--  1 yoku0825 yoku0825  24023347 Nov  5  2013 mysql-5.1.73.tar.gz
-rw-r--r--  1 yoku0825 yoku0825  21111902 Aug 29  2018 mysql-5.5.62.tar.gz
-rw-r--r--  1 yoku0825 yoku0825  32411131 Jan  5  2021 mysql-5.6.51.tar.gz
drwxr-xr-x 34 yoku0825 yoku0825      4096 Jul 31 17:54 mysql-5.7.44
drwxr-xr-x 36 yoku0825 yoku0825      4096 Dec  4 18:42 mysql-8.0.40
-rw-r--r--  1 yoku0825 yoku0825 425791134 Jul 13 02:50 mysql-8.4.2.tar.gz
drwxr-xr-x 35 yoku0825 yoku0825      4096 Dec  3 18:01 mysql-8.4.3
-rw-r--r--  1 yoku0825 yoku0825 465097732 Sep 17 18:12 mysql-8.4.3.tar.gz
-rw-r--r--  1 yoku0825 yoku0825 428335150 Jul 13 02:56 mysql-9.0.1.tar.gz
drwxr-xr-x 36 yoku0825 yoku0825      4096 Nov 11 13:16 mysql-9.1.0
-rw-r--r--  1 yoku0825 yoku0825 480032080 Sep 24 19:09 mysql-9.1.0.tar.gz
-rw-r--r--  1 yoku0825 yoku0825  53298645 Oct 11  2023 mysql-boost-5.7.44.tar.gz
-rw-r--r--  1 yoku0825 yoku0825 490502884 Sep 18 17:38 mysql-boost-8.0.40.tar.gz
drwxr-xr-x  3 yoku0825 yoku0825      4096 Dec  5 14:32 mysql-sandboxes
drwxr-xr-x 17 yoku0825 yoku0825      4096 Dec  5 14:35 mysql-shell-8.4.3-src
-rw-r--r--  1 yoku0825 yoku0825 152976386 Sep 18 13:36 mysql-shell-8.4.3-src.tar.gz
-rw-r--r--  1 yoku0825 yoku0825   3330656 Dec  7  2021 mysql_random_data_load_0.1.12_Linux_x86_64.tar.gz
-rw-r--r--  1 yoku0825 yoku0825     25554 Mar 29  2022 mysqlbench-0.1.tgz

ビルドはパス以外デフォルトで、INSTALL_LAYOUTもSTANDALONEなので /usr/mysql/x.y.z の下に data ができてそこがdatadirになります。tmpdirは全部 /tmp ですね。
CMAKE_BUILD_TYPEはデフォルトでRelWithDebInfo, MINIMAL_RELWITHDEBINFOだけ明示的にOFFにしてあります。

容量の問題だったり、軽く100万件突っ込むだけで時間がかかってしまうので デバッグビルドではない です(一時期やってたけどやめた)

多少データが入ってるかも知れませんがこんな感じ。

]$ du -sh /usr/mysql/*
86M     /usr/mysql/5.0.96
248M    /usr/mysql/5.1.73
371M    /usr/mysql/5.5.62
1.1G    /usr/mysql/5.6.51
3.7G    /usr/mysql/5.7.44
3.5G    /usr/mysql/8.0.40
3.3G    /usr/mysql/8.4.3
3.4G    /usr/mysql/9.1.0

MySQL Shell以外ではもうとっくにメンテナンスされてない MySQL::Sandbox を使っています (後継の dbdeployer には移らないままdbdeployerもメンテナンスが終わってしまった)

バージョンを打ち分けられるように SANDBOX_BINARY=/usr/mysql を設定しています。

$ make_replication_sandbox --how_many_slaves=1 8.0.40
..

MySQL 8.4とそれ以降は動きません。 ここ だけ修正してもダメです。
CHANGE REPLICATION SOURCE TO に対応していないからです。
本格的に8.4でやるようになったら自分のぶんだけ直すかもしれない。
(今はMySQL Shellのサンドボックスか↓のDockerコンテナと InnoDB ReplicaSet でなんとなくやりたいことはやれている)

自分でビルドしたバイナリは容量の問題で1つ新しいのが出たらすぐ前の世代は消しちゃいますが、たまに試したくなるのでそういう時に便利なのがDockerですね。

$ docker images
REPOSITORY                                             TAG           IMAGE ID       CREATED         SIZE
ubuntu                                                 24.04         fec8bfd95b54   7 weeks ago     78.1MB
container-registry.oracle.com/mysql/community-server   8.4.3         4e4fdc4a92c8   7 weeks ago     579MB
container-registry.oracle.com/mysql/community-server   8.0.40        cbe43d82e50c   7 weeks ago     576MB
implem/pleasanter                                      codedefiner   4255febbda1b   8 weeks ago     416MB
implem/pleasanter                                      latest        5b7646c9bded   8 weeks ago     460MB
container-registry.oracle.com/mysql/community-server   8.0.39        15df6905a022   4 months ago    551MB
container-registry.oracle.com/mysql/community-server   8.4.2         a50438f82562   4 months ago    562MB
container-registry.oracle.com/mysql/community-server   8.0.38        d37c2d1560e3   5 months ago    551MB
quay.io/centos/centos                                  stream8       197af1ac4595   6 months ago    218MB
container-registry.oracle.com/mysql/community-server   8.4.0         8d0dfbc1b8b6   7 months ago    564MB
container-registry.oracle.com/mysql/community-server   8.3.0         c0db6c8ff4af   10 months ago   691MB
rockylinux                                             9             9cc24f05f309   12 months ago   176MB
container-registry.oracle.com/mysql/community-server   8.2.0         39654aa412c0   13 months ago   596MB
container-registry.oracle.com/mysql/community-server   8.1.0         902aecd50aa8   16 months ago   566MB
mysql/mysql-server                                     5.7.41        a4ad24fe52cd   22 months ago   432MB
container-registry.oracle.com/mysql/community-server   8.0.28        12a2858989df   2 years ago     417MB
centos                                                 7             eeb6ee3f44bd   3 years ago     204MB

私は dmysql x.y.z でそのインスタンスが1つ浮かせられるように dmysql というbash functionを作っています。

dmysql ()
{
    version_str="$1";
    shift;
    [[ -z $version_str ]] && version_str="latest";
    version_int=$(echo $version_str | awk -F"[.-]" '{printf("%d%02d%02d\n", $1, $2, $3)}');
    if [[ $version_str = "latest" || $version_int -ge 80022 ]]; then
        repo="container-registry.oracle.com/mysql/community-server";
    else
        repo="mysql/mysql-server";
    fi;
    docker run -d --restart=on-failure -P -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_ROOT_PASSWORD="""" -e MYSQL_ROOT_HOST=""%"" $repo:$version_str $@
}

ifが噛ませてあるのは 日々の覚書: MySQL公式のDockerリポジトリがcontainer-registry.oracle.comに引っ越していた からです。

これでだいたいの5.6から9.1(5.7の一部だけどっちのdockerリポジトリにもない…)は一発で起動できるので便利ですね。

変わり種としては、一番多く使う /usr/mysql の下は mysqld_multi で起動していることです。

$ mysqld_multi start 80
$ mysqld_multi start 56,57

こんなコマンドになります。 /etc/my.cnf がこんな感じになっていて

[mysqld50]
server-id  = 1050
basedir    = /usr/mysql/5.0.96
mysqld     = /usr/mysql/5.0.96/libexec/mysqld
socket     = /usr/mysql/5.0.96/var/mysql.sock
port       = 64050
pid-file   = /usr/mysql/5.0.96/var/mysql.pid
datadir    = /usr/mysql/5.0.96/var

[mysqld51]

..
[mysqld84]
server-id  = 1084
basedir    = /usr/mysql/8.4.3
mysqld     = /usr/mysql/8.4.3/bin/mysqld_safe
socket     = /usr/mysql/8.4.3/data/mysql.sock
port       = 64084
pid-file   = /usr/mysql/8.4.3/data/mysql.pid
datadir    = /usr/mysql/8.4.3/data
mysqlx= off
#mysql_native_password = ON
admin_address= 127.0.0.1
admin_port= 63084

[mysqld91]
server-id  = 1091
basedir    = /usr/mysql/9.1.0
mysqld     = /usr/mysql/9.1.0/bin/mysqld_safe
socket     = /usr/mysql/9.1.0/data/mysql.sock
port       = 64091
pid-file   = /usr/mysql/9.1.0/data/mysql.pid
datadir    = /usr/mysql/9.1.0/data
mysqlx= off
admin_address= 127.0.0.1
admin_port= 63091

[mysqldXX] の部分が mysqld_multi の引数になるとこです。
もともとは別にバージョンじゃなくてdatadirとか分けてサンドボックスを起動/終了するためのものだと思うんですが、バージョン分けて起動するのに具合が良かったので長らく使っています。

実は mysqladmin ( mysqld_multi が内部的に使う ) には非互換があって、一応こんな風に分岐しています。

multi ()
{
    if [[ "$2" -lt "80" ]]; then
        multi=/usr/mysql/5.6.51/bin/mysqld_multi;
    else
        multi=/usr/mysql/8.0.40/bin/mysqld_multi;
    fi;
    case "$1" in
        "restart")
            shift;
            $multi stop $*;
            sleep 10;
            $multi start $*
        ;;
        *)
            $multi $*
        ;;
    esac
}

人と変わってるところはこれくらいかなあ…?
あとは普通です、たぶん(perlのDBD::mysqlを8.4のlibmysqlclientでビルドしてるくらい?)

あ、MySQL 3.23をインストールしたのもこのインスタンスです :D

明日は takaidohigashiさん です!