GA

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さん です!

2024/12/04

ConoHa VPSでMySQLをビルドする 2024年

この記事は ConoHa Advent Calendar 2024 の4日目の記事で、 MySQL Advent Calendar 2024 の4日目の記事です。

ConoHa Advent Calendarの昨日の記事は narikakunさんConoHa VPSを使っておひとり様Misskeyサーバーを作る , MySQL Advent Calendarの昨日の記事は hmatsu47さん今年 1 年の個人的な MySQL 関連ネタ登壇を振り返る でした。


去年までのあらすじ。

今年はRocky Linux9でビルドします。

普段使いは今もまだVer. 2.0のVPSを使っているけれど、せっかくなのでVer. 3.0のマシンを使ってみました。性能比較とかはしない。プランは メモリ 1GB/CPU 2Core で変わらず。


$ cat /etc/os-release

NAME="Rocky Linux"

VERSION="9.4 (Blue Onyx)"

ID="rocky"

ID_LIKE="rhel centos fedora"

VERSION_ID="9.4"

PLATFORM_ID="platform:el9"

PRETTY_NAME="Rocky Linux 9.4 (Blue Onyx)"

ANSI_COLOR="0;32"

LOGO="fedora-logo-icon"

CPE_NAME="cpe:/o:rocky:rocky:9::baseos"

HOME_URL="https://rockylinux.org/"

BUG_REPORT_URL="https://bugs.rockylinux.org/"

SUPPORT_END="2032-05-31"

ROCKY_SUPPORT_PRODUCT="Rocky-Linux-9"

ROCKY_SUPPORT_PRODUCT_VERSION="9.4"

REDHAT_SUPPORT_PRODUCT="Rocky Linux"

REDHAT_SUPPORT_PRODUCT_VERSION="9.4"

まずメインストリームではなくなったけどまだ現役のMySQL 8.0.40

$ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-boost-8.0.40.tar.gz
$ tar xf mysql-boost-8.0.40.tar.gz
$ cd mysql-8.0.40/

cmakeが無かったので入れる。もうcmake3じゃなくてcmakeだけで3系が入るのね。

$ cmake .
-bash: cmake: command not found

$ sudo dnf install cmake

$ cmake .
-- Running cmake version 3.26.5
-- Could NOT find Git (missing: GIT_EXECUTABLE)
-- This is .el9. as found from 'rpm -qf /'
-- Looking for a devtoolset compiler
CMake Warning at CMakeLists.txt:407 (MESSAGE):
  Could not find devtoolset compiler/linker in /opt/rh/gcc-toolset-12

CMake Warning at CMakeLists.txt:409 (MESSAGE):
  You need to install the required packages:

   yum install gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils gcc-toolset-12-annobin-annocheck gcc-toolset-12-annobin-plugin-gcc

CMake Error at CMakeLists.txt:411 (MESSAGE):
  Or you can set CMAKE_C_COMPILER and CMAKE_CXX_COMPILER explicitly.

-- Configuring incomplete, errors occurred!

他にgcc-toolset(devtoolsetではなくなったのか)が必要だと言われるのでインストール。
yumでまだ動くのかとは思ったけどシンボリックリンクでdnf-3に渡されてた。

$ sudo yum install gcc-toolset-12-gcc gcc-toolset-12-gcc-c++ gcc-toolset-12-binutils gcc-toolset-12-annobin-annocheck gcc-toolset-12-annobin-plugin-gcc

$ cmake .
..
CMake Error at CMakeLists.txt:642 (MESSAGE):
  Please do not build in-source.  Out-of source builds are highly
  recommended: you can have multiple builds for the same source, and there is
  an easy way to do cleanup, simply remove the build directory (note that
  'make clean' or 'make distclean' does *not* work)

  You *can* force in-source build by invoking cmake with
  -DFORCE_INSOURCE_BUILD=1

-- Configuring incomplete, errors occurred!

ビルドディレクトリを分けないといけないアレ。面倒だけど分けておく(いつもは -DFORCE_INSOUCE_BUILD=1 派)

$ cd ../
$ mkdir 8.0.40-build
$ cd 8.0.40-build

$ cmake ../mysql-8.0.40
..
CMake Error at CMakeLists.txt:642 (MESSAGE):
  Please do not build in-source.  Out-of source builds are highly
  recommended: you can have multiple builds for the same source, and there is
  an easy way to do cleanup, simply remove the build directory (note that
  'make clean' or 'make distclean' does *not* work)

  You *can* force in-source build by invoking cmake with
  -DFORCE_INSOURCE_BUILD=1

-- Configuring incomplete, errors occurred!

む、 mysql-8.0.40/CMakeCache.txt が変なのを残している気がするので消す。

$ rm ../mysql-8.0.40/CMakeCache.txt
$ cmake ../mysql-8.0.40
..
CMake Error at cmake/boost.cmake:109 (MESSAGE):
  You can download it with -DDOWNLOAD_BOOST=1 -DWITH_BOOST=<directory>

  This CMake script will look for boost in <directory>.  If it is not there,
  it will download and unpack it (in that directory) for you.

  You can also download boost manually, from
  https://archives.boost.io/release/1.77.0/source/boost_1_77_0.tar.bz2

  If you are inside a firewall, you may need to use an https proxy:

  export https_proxy=http://example.com:80

Call Stack (most recent call first):
  cmake/boost.cmake:278 (COULD_NOT_FIND_BOOST)
  CMakeLists.txt:1593 (INCLUDE)

boost同梱版をダウンロードしていたけど -DWITH_BOOST を指定し忘れていたというアレ。

$ cmake -DWITH_BOOST=../mysql-8.0.40/boost ../mysql-8.0.40

..
Not a supported openssl version in WITH_SSL=system.
Make sure you have specified a supported SSL version.
Valid options are :
openssl[0-9]+ (use alternative system library)
yes (synonym for system)
</path/to/custom/openssl/installation>

CMake Error at cmake/ssl.cmake:84 (MESSAGE):
  Please install the appropriate openssl developer package.

Call Stack (most recent call first):
  cmake/ssl.cmake:380 (FATAL_SSL_NOT_FOUND_ERROR)
  CMakeLists.txt:1825 (MYSQL_CHECK_SSL)

openssl-develが入っていなかったアレ。

$ sudo dnf install -y openssl-devel
$ cmake -DWITH_BOOST=../mysql-8.0.40/boost ../mysql-8.0.40

..
CMake Error at cmake/readline.cmake:93 (MESSAGE):
  Curses library not found.  Please install appropriate package,

      remove CMakeCache.txt and rerun cmake.On Debian/Ubuntu, package name is libncurses5-dev, on Redhat and derivates it is ncurses-devel.
Call Stack (most recent call first):
  cmake/readline.cmake:127 (FIND_CURSES)
  cmake/readline.cmake:221 (MYSQL_USE_BUNDLED_EDITLINE)
  CMakeLists.txt:1929 (MYSQL_CHECK_EDITLINE)

今度はncurses-develが入っていなかったアレ(しかし何か足りないにせよ毎回エラーメッセージが違うなあ…)

$ sudo dnf install -y ncurses-devel
$ cmake -DWITH_BOOST=../mysql-8.0.40/boost ../mysql-8.0.40

..
-- Checking for module 'libtirpc'
--   Package 'libtirpc', required by 'virtual:world', not found
CMake Warning at cmake/rpc.cmake:41 (MESSAGE):
  Cannot find RPC development libraries.  You need to install the required
  packages:

    Debian/Ubuntu:              apt install libtirpc-dev
    RedHat/Fedora/Oracle Linux: yum install libtirpc-devel
    SuSE:                       zypper install glibc-devel

Call Stack (most recent call first):
  cmake/rpc.cmake:107 (WARN_MISSING_SYSTEM_TIRPC)
  CMakeLists.txt:2065 (MYSQL_CHECK_RPC)

CMake Error at cmake/rpc.cmake:108 (MESSAGE):
  Could not find rpc/rpc.h in /usr/include or /usr/include/tirpc
Call Stack (most recent call first):
  CMakeLists.txt:2065 (MYSQL_CHECK_RPC)

前はncurses-devel入れた後にCMakeCache.txtを消さないといけなかったような気がするけどそのままいけて次のエラー。libtirpc-develでしょ。 知ってるよ。

$ sudo dnf install -y libtirpc-devel

Last metadata expiration check: 0:27:55 ago on Mon 02 Dec 2024 04:29:48 PM JST.
No match for argument: libtirpc-devel
Error: Unable to find a match: libtirpc-devel

$ sudo dnf install -y --enablerepo=crb libtirpc-devel

$ cmake -DWITH_BOOST=../mysql-8.0.40/boost ../mysql-8.0.40

..
CMake Warning at cmake/rpc.cmake:30 (MESSAGE):
  Cannot find rpcgen executable.  You need to install the required packages:

    Debian/Ubuntu:              apt install rpcsvc-proto
    RedHat/Fedora/Oracle Linux: yum install rpcgen
    SuSE:                       zypper install glibc-devel

Call Stack (most recent call first):
  plugin/group_replication/libmysqlgcs/cmake/rpcgen.cmake:113 (WARN_MISSING_RPCGEN_EXECUTABLE)
  plugin/group_replication/libmysqlgcs/CMakeLists.txt:51 (INCLUDE)

CMake Error at plugin/group_replication/libmysqlgcs/cmake/rpcgen.cmake:114 (MESSAGE):
  Could not find rpcgen
Call Stack (most recent call first):
  plugin/group_replication/libmysqlgcs/CMakeLists.txt:51 (INCLUDE)

rpcgen。これも知ってる。

$ sudo dnf install -y --enablerepo=crb rpcgen

$ cmake -DWITH_BOOST=../mysql-8.0.40/boost ../mysql-8.0.40
..

-- Build files have been written to: /home/yoku0825/8.0.40-build

じゃあmake。メモリ 1GB/CPU 2CoreのプランだとOOM Killerでやられるかswapファイルを積んでもスラッシングでやられるだけなので大人しく1コアで。

$ time make
..
..
[ 56%] Generating xdr_gen/xcom_vp.h, xdr_gen/xcom_vp_xdr.c
cannot find C preprocessor: cpp
/usr/bin/rpcgen: C preprocessor failed with exit code 1
cannot find C preprocessor: cpp
/usr/bin/rpcgen: C preprocessor failed with exit code 1
cannot find C preprocessor: cpp
/usr/bin/rpcgen: C preprocessor failed with exit code 1
cannot find C preprocessor: cpp
/usr/bin/rpcgen: C preprocessor failed with exit code 1
cannot find C preprocessor: cpp
..
make[2]: *** [plugin/group_replication/libmysqlgcs/CMakeFiles/mysqlgcs.dir/build.make:99: plugin/group_replication/libmysqlgcs/CMakeFiles/mysqlgcs.dir/src/bindings/xcom/xcom/pax_msg.cc.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:14466: plugin/group_replication/libmysqlgcs/CMakeFiles/mysqlgcs.dir/all] Error 2
make: *** [Makefile:166: all] Error 2

real    131m30.750s
user    70m33.106s
sys     6m43.641s

たっぷり時間をかけてからエラった。cppコマンドが見つからない。

$ sudo dnf install cpp

$ time make
..
[ 56%] Built target gr_unit_test_resource
[ 56%] Building CXX object plugin/group_replication/libmysqlgcs/CMakeFiles/mysqlgcs.dir/src/bindings/xcom/xcom/pax_msg.cc.o
In file included from /home/yoku0825/mysql-8.0.40/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/pax_msg.cc:31:
/home/yoku0825/mysql-8.0.40/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/app_data.h:44:1: error: ‘app_data_ptr’ does not name a type
   44 | app_data_ptr clone_app_data(app_data_ptr a);
      | ^~~~~~~~~~~~
/home/yoku0825/mysql-8.0.40/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/app_data.h:45:1: error: ‘app_data_ptr’ does not name a type
   45 | app_data_ptr clone_app_data_single(app_data_ptr a);
      | ^~~~~~~~~~~~

..
make[2]: *** [plugin/group_replication/libmysqlgcs/CMakeFiles/mysqlgcs.dir/build.make:99: plugin/group_replication/libmysqlgcs/CMakeFiles/mysqlgcs.dir/src/bindings/xcom/xcom/pax_msg.cc.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:14466: plugin/group_replication/libmysqlgcs/CMakeFiles/mysqlgcs.dir/all] Error 2
make: *** [Makefile:166: all] Error 2

real    0m15.112s
user    0m7.916s
sys     0m3.575s

同じところですぐ転けた。たぶんCMakeFilesディレクトリに変なのが残ってる。
plugin/group_replication/CMakeFiles だけ消せばいい気がするんだけど、間違えて plugin/group_replication/libmysqlgcs を消してしまったので最初から…。

$ make clean
$ time make
..
real    248m20.672s
user    136m41.102s
sys     11m49.462s

たっぷり4時間かけて成功。

$ sudo make install

$ du -sh /usr/local/mysql
1.5G    /usr/local/mysql

8.0.40はこんなもんだったので次。メインストリームの8.4.3。8.4からは(途中のInnovation Releaseからだったかもしれないけどおぼえてない) boost同梱版という概念がなくなって必ず同梱されている。

$ sudo rm -r /usr/local/mysql
$ cd
$ wget https://dev.mysql.com/get/Downloads/MySQL-8.4/mysql-8.4.3.tar.gz

$ tar xf mysql-8.4.3.tar.gz

$ mkdir 8.4.3-build
$ cd 8.4.3-build/
$ cmake ../mysql-8.4.3

8.0.40の時にインストールした諸々だけでcmakeには成功。

$ time make
..
real    310m22.856s
user    164m47.727s
sys     14m17.480s

$ du -sh /usr/local/mysql
1.5G    /usr/local/mysql

うひー6時間オーバー。バイナリサイズは変わらず1.5GB程度…。
最後にInnovation Releaseの9.1.0。

$ sudo rm -r /usr/local/mysql
$ cd
$ wget https://dev.mysql.com/get/Downloads/MySQL-9.1/mysql-9.1.0.tar.gz

$ tar xf mysql-9.1.0.tar.gz
$ mkdir 9.1.0-build
$ cd 9.1.0-build
$ cmake ../mysql-9.1.0
-- Running cmake version 3.26.5
-- Could NOT find Git (missing: GIT_EXECUTABLE)
-- This is .el9. as found from 'rpm -qf /'
-- Looking for a devtoolset compiler
CMake Warning at CMakeLists.txt:396 (MESSAGE):
  Could not find devtoolset compiler/linker in /opt/rh/gcc-toolset-13

CMake Warning at CMakeLists.txt:398 (MESSAGE):
  You need to install the required packages:

   yum install gcc-toolset-13-gcc gcc-toolset-13-gcc-c++ gcc-toolset-13-binutils gcc-toolset-13-annobin-annocheck gcc-toolset-13-annobin-plugin-gcc

CMake Error at CMakeLists.txt:400 (MESSAGE):
  Or you can set CMAKE_C_COMPILER and CMAKE_CXX_COMPILER explicitly.
-- Configuring incomplete, errors occurred!

9.1はtoolset-12じゃなくて13が必要らしい。

$ sudo yum install gcc-toolset-13-gcc gcc-toolset-13-gcc-c++ gcc-toolset-13-binutils gcc-toolset-13-annobin-annocheck gcc-toolset-13-annobin-plugin-gcc

$ cmake ../mysql-9.1.0
$ time make

..
real    332m51.066s
user    180m36.865s
sys     16m31.190s

$ sudo make install
$ du -sh /usr/local/mysql
1.5G    /usr/local/mysql

MySQL 9.1に至っては6時間半かかった…。
早めに始めておいてよかった…。

明日はConoHa Advent Calendaryamagami2211さん, MySQL Advent Calendarが……募集中です!