Quantcast
Channel: 日々の覚書

レプリケーション構成でない2つのMySQLに確実にデータを書き込みたいメモ

$
0
0
  • それぞれのMySQLはSemisyncで正しくフェイルオーバーしたりGroup Replicationだったりで書き込みは信用できることにする
  • サロゲートキー(だけ)でなくて比較可能なPRIMARY KEYが存在することにする
  • 取り敢えず簡単なINSERTで考えるけどUPDATEもありそう

という前提で考え事。
FEDERATEDストレージエンジンはそもそもトランザクションをサポートしないので論外とする。

App側の2層コミットっぽいもの

2 Phase Commitはそもそもコーディネーターの障害に対する耐性がない…。

eval
{
  $dbh1->begin;
  $dbh1->do("INSERT INTO ..");
  $dbh2->begin;
  $dbh2->do("INSERT INTO ..");
};

if ($@)
{
  $dbh1->rollback;
  $dbh2->rollback;
}

$dbh1->commit;

### この瞬間にAppのプロセスが落ちるとダメ
$dbh2->commit;

この場合「必ずdbh2の方が足りない状態になる」のは確かかもしれない。

補償トランザクション的な何か

さっきののcommitの部分にさらに一捻り…したところで

eval
{
  $dbh1->commit;
};
$dbh2->rollback if $@;

eval
{
  $dbh2->commit;
};

if ($@)
{
  $dbh1->begin;
  ### ここらへんで落ちるとやっぱりダメ
  $dbh1->do("DELETE FROM ..");
  $dbh1->commit;
}

補償トランザクション形式だとUPDATEの巻き戻しがなかなか大変そうだし、結局落ちるとダメにはなる。この場合もdbh2が足りない状態になるのは当たりだろうか。
ちなみに MySQLのXAステートメントを使ったところでxidをどこかに記録しておかないと処理が引き継げないので結局何も記録せずに単一プロセスでやろうというのは無理。

さっきのと合わせて、dbh1の側の created_atみたいなのにインデックスを張っておいて定期的にスキャンして突合するような感じになるはず。DELETEまで考えるとdbh2側をスキャンしてdbh1側をNested Loopスキャンもしないといけなくなる(何がなくなったのかは片方だけ見てもわからないから)

非同期ダブルライト型

少なくとも片方のMySQLにはある程度確実に書ける(書けなければちゃんとエラーになる(実際semisyncではこれは保証できないんだけどいったん置いておく)、書いた以上はMySQL側がクラッシュしてフェイルオーバーしても残る)という前提に基づくと、

$dbh1->begin;
eval
{
  $dbh1->do("INSERT INTO metadata ..");
  $dbh1->do("INSERT INTO real_table ..");
  $dbh1->commit;
};

$dbh1->rollback if $@;

メタデータ(例えばdbh2に発行するべきINSERT文そのものでもいい)のテーブルへの記録と、dbh1側の書き込みたいテーブルを1つのトランザクションにまとめると、この2つのテーブルに関してはトランザクションで保護される。
$dbh1->commitまでにAppがクラッシュした場合はAppとしてエラーになってメタデータもテーブルも残っていない状態になるし、 $dbh1->commitまで到達した場合は両方とも残っている。
で、別途ワーカーが必要。

$dbh1->begin;
my $sth= $dbh1->prepare("SELECT * FROM metadata ORDER BY id ASC LIMIT 1 FOR UPDATE SKIP LOCKED");
my $row= $sth->selectrow_hashref;
eval
{
  $dbh2->begin;
  $dbh2->do("INSERT INTO .."); 
  $dbh2->commit;
}
$dbh2->rollback if $@;
### ここらへんで落ちるとmetadataだけおかしくなる
$dbh1->do("DELETE FROM metadata ..");
$dbh1->commit;

この場合、

  • ワーカーが動くまではdbh1とdbh2で不整合が起こり続ける(dbh2が足りない状態になる)こと
  • ワーカーがクラッシュするとmetadataだけ消えずに余ること
    なのでmetadataと実際のテーブルの更新情報を突合するためのキーは必ず必要になって(dbh1に記録する時にApp側でUUID生成して、実テーブルの方に last_updated_by = ?みたいに持つとか)ワーカーが同じ更新を二重実行しても安全になるようにしてやらないといけない。
    けれどスキャンは必要なさそうだしワーカーの起動が制御しやすそうだなと思ったのでした。

まるでmysql.slave_relay_log_infoとSQLスレッドだな、とは思った。
ただの考え事でした。


【2025/05/21 21:22】

「非同期ダブルライト型」と呼んでいたやつは「Transactional Outboxパターン」という名前がついていると教えてもらいました! ありがとうございますm(_ _)m



トランザクションアウトボックスパターン - AWS 規範ガイダンス


Re: セキュアそうでセキュアじゃない少しセキュアな気分になれるmysql_config_editorに直接--passwordを指定できるようにしてみた

$
0
0


  • mysql_config_editor--password, -pだけは引数を受け付けられなくて、常にプロンプトでパスワードを要求する

    • それはそれでいいんだけど、スクリプトとかで動的に .my.login.cnfを作るのに面倒
    • なので --passwordをオプションで受け付けられるようなパッチを書いてみた
  • .mylogin.cnfは鍵なしでパラメータを読み出せる (パスワードがマスクされているのは単に平文で読みだした後に mysql_config_editor自身がマスクしているだけ)ので、いじるついでにこれも出せるようにしてみた

    • ちなみに my_print_defaults -s "セクション名"ならマスクもせずに取り出せる( my_print_defaultsでいけるって知らなくて足してしまった…)

パッチはこれ。MySQL 5.7.44の mysql_config_editorベース。
mysql_config_editor_patch

$ cd /path/to/mysql-5.7.44
$ wget https://gist.githubusercontent.com/yoku0825/6d8a2fee64de1b687f7c41bd6052f69e/raw/f860e29acb41990c055b56c73e06a5008bf4c481/mysql_config_editor_patch -O mysql_config_editor.patch
$ patch --backup client/mysql_config_editor.cc mysql_config_editor.patch
$ make mysql_config_editor
$ client/mysql_config_editor set --login-path=yoku0825 -uabc -pdef
$ client/mysql_config_editor print --all
[yoku0825]
user = abc
password = def

Oracle Linux 8でMySQL 5.7のrpmをビルドする

$
0
0

TL;DR


MySQL Product Archivesで見たら、MySQL 5.7はOracle Linux(に限らずRHEL系の)8に対応したrpmはなかった。けど欲しい。

というわけで(el7用のrpmファイルでも動くような気はするけど一応) Oracle Linuxのコンテナイメージの中でrpmbuildするメモ。

# wget http://repo.mysql.com/yum/mysql-5.7-community/el/7/SRPMS/mysql-community-5.7.44-1.el7.src.rpm
# rpm -i mysql-community-5.7.44-1.el7.src.rpm

# dnf install rpm-build

# rpmbuild -bb ~/rpmbuild/SPECS/mysql.spec
error: Failed build dependencies:
        cyrus-sasl-devel is needed by mysql-community-5.7.44-1.el8.x86_64
        libaio-devel is needed by mysql-community-5.7.44-1.el8.x86_64
        ncurses-devel is needed by mysql-community-5.7.44-1.el8.x86_64
        numactl-devel is needed by mysql-community-5.7.44-1.el8.x86_64
        openldap-devel is needed by mysql-community-5.7.44-1.el8.x86_64
        openssl-devel is needed by mysql-community-5.7.44-1.el8.x86_64
        perl is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Carp) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Config) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Cwd) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Data::Dumper) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(English) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Errno) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Exporter) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Fcntl) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(File::Basename) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(File::Copy) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(File::Find) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(File::Path) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(File::Spec) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(File::Spec::Functions) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(File::Temp) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Getopt::Long) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(IO::File) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(IO::Handle) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(IO::Pipe) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(IO::Select) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(IO::Socket) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(IO::Socket::INET) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(JSON) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Memoize) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(POSIX) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Sys::Hostname) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Time::HiRes) is needed by mysql-community-5.7.44-1.el8.x86_64
        perl(Time::localtime) is needed by mysql-community-5.7.44-1.el8.x86_64
        time is needed by mysql-community-5.7.44-1.el8.x86_64
        zlib-devel is needed by mysql-community-5.7.44-1.el8.x86_64

依存のものを入れる。

# dnf install -y cyrus-sasl-devel libaio-devel ncurses-devel numactl-devel openldap-devel openssl-devel perl time zlib-devel perl-JSON

# rpmbuild -bb ~/rpmbuild/SPECS/mysql.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.5u4iqi
+ umask 022
+ cd /root/rpmbuild/BUILD
+ cd /root/rpmbuild/BUILD
+ rm -rf mysql-5.7.44
+ /usr/bin/mkdir -p mysql-5.7.44

..
+ cmake ../mysql-5.7.44 -DBUILD_CONFIG=mysql_release -DINSTALL_LAYOUT=RPM -DCMAKE_BUILD_TYPE=Debug -DWITH_BOOST=.. '-DCMAKE_C_FLAGS= -g -pipe -Wall -Werror=format-security   -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection''-DCMAKE_CXX_FLAGS= -g -pipe -Wall -Werror=format-security   -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection' -DWITH_INNODB_MEMCACHED=1 -DINSTALL_LIBDIR=lib64/mysql -DINSTALL_PLUGINDIR=lib64/mysql/plugin/debug -DMYSQL_UNIX_ADDR=/var/lib/mysql/mysql.sock -DMYSQLX_UNIX_ADDR=/var/run/mysqld/mysqlx.sock -DFEATURE_SET=community -DWITH_EMBEDDED_SERVER=1 -DWITH_EMBEDDED_SHARED_LIBRARY=1 -DWITH_NUMA=ON '-DCOMPILATION_COMMENT=MySQL Community Server - Debug (GPL)' -DMYSQL_SERVER_SUFFIX=
/var/tmp/rpm-tmp.AHE541: line 47: cmake: command not found
error: Bad exit status from /var/tmp/rpm-tmp.AHE541 (%build)

RPM build errors:
    Bad exit status from /var/tmp/rpm-tmp.AHE541 (%build)

cmake忘れてた…というかBuildRequiresに書いておいてくれればいいのに。そのほか、ビルド中に転けたパッケージも入れた。

# dnf install -y cmake gcc gcc-c++ libtirpc-devel
# dnf install -y --enablerepo=ol8_codeready_builder rpcgen
# rpmbuild -bb ~/rpmbuild/SPECS/mysql.spec

これで完成。

# ll ~/rpmbuild/RPMS/x86_64/mysql-community-*
-rw-r--r--. 1 root root  22908224 Jun 10 03:31 /root/rpmbuild/RPMS/x86_64/mysql-community-client-5.7.44-1.el8.x86_64.rpm
-rw-r--r--. 1 root root    323348 Jun 10 03:31 /root/rpmbuild/RPMS/x86_64/mysql-community-common-5.7.44-1.el8.x86_64.rpm
-rw-r--r--. 1 root root   3790152 Jun 10 03:32 /root/rpmbuild/RPMS/x86_64/mysql-community-devel-5.7.44-1.el8.x86_64.rpm
-rw-r--r--. 1 root root  59816800 Jun 10 03:32 /root/rpmbuild/RPMS/x86_64/mysql-community-embedded-5.7.44-1.el8.x86_64.rpm
-rw-r--r--. 1 root root 172178112 Jun 10 03:33 /root/rpmbuild/RPMS/x86_64/mysql-community-embedded-devel-5.7.44-1.el8.x86_64.rpm
-rw-r--r--. 1 root root   1866328 Jun 10 03:32 /root/rpmbuild/RPMS/x86_64/mysql-community-libs-5.7.44-1.el8.x86_64.rpm
-rw-r--r--. 1 root root 166402044 Jun 10 03:31 /root/rpmbuild/RPMS/x86_64/mysql-community-server-5.7.44-1.el8.x86_64.rpm
-rw-r--r--. 1 root root 150149116 Jun 10 03:32 /root/rpmbuild/RPMS/x86_64/mysql-community-test-5.7.44-1.el8.x86_64.rpm

バックアップをネットワークまたいだファイルサーバに置きたいけど指定した以外のファイルは上書きもされたくないし読まれたくもない

$
0
0

TL;DR


普段から使っていたのはファイルサーバー側でポートを開いてリダイレクトしちゃう。実際にはもう少し「ランダムにポートを開いてその番号を教えてくれるスクリプトだけをssh越しに実行させる」とか「curlで叩くとトークン検証してランダムにポートを開いてその番号を教えてくれるAppにする」とかの工夫はできる。

nc -l 13306 > /path/to/myfile

バックアップはリモートから書き込む。bash使いなので /dev/tcpは好き。

xtrabackup -S /usr/mysql/8.4.5/data/mysql.sock -uroot --backup --stream=xbstream | pv >  /dev/tcp/file_server/13306

書き込みが終わる(異常終了を含む)とncプロセスは落ちるので再利用のためにはレシーバー側でncの再実行が必要。そのタイミングでリダイレクト先のファイル名を変更できるので上書きも回避できる(noclobberでエラーにするか、別のファイルとして受けるかの違いではある)

もし通信経路を通す前に圧縮したいなら送信元で圧縮すればいいし、

xtrabackup -S /usr/mysql/8.4.5/data/mysql.sock -uroot --backup --stream=xbstream | pzstd -c | pv  > /dev/tcp/file_server/13306

受信側でそのまま展開したいならncからパイプしてやればいい(xbstreamまではパイプで繋げるけどxtrabackup —prepareは別でやらないといけない)

nc -l 13306 | pzstd -dc | xbstream -x

受ける側でパスワードをかけたりもした(ランダムに推測不可能なものを作ってDBAにだけメールしてた気がする)

nc -l 13306 | openssl aes-256-cbc -pbkdf2 -e -pass pass:something > /tmp/myfile.enc

$ file /tmp/myfile.enc
/tmp/myfile.enc: openssl enc'd data with salted password

$ openssl aes-256-cbc -pbkdf2 -d -in /tmp/myfile.enc -out /tmp/myfile
enter AES-256-CBC decryption password:

$ pzstd -dc /tmp/myfile | xbstream -x
/tmp/myfile         : 75159378 bytes

デーモンにしたりすると面倒だけど後々が楽。。

バイナリログのバックアップについて考える in 2025

$
0
0

rsync

シェルが必要(たとえば ~/.ssh/authorized_keysで mysqldの実効アカウントにログインできるとか)なので新規に採用は嫌な感じ。
それを飲めるなら候補の一つとしてアリな気はする。
帯域ネックなら -zで圧縮をかけるとCPUを使いつつ転送に圧縮をかけられるが、転送後のファイルは圧縮されていない状態で現れるので保管を考えると更に圧縮する必要があって手間は手間。 binlog_transaction_compressionが有効なら最初から圧縮されているのでベター。
binlog暗号化をされている状態でも保管はできるが、暗号化されたバイナリログを読み出すためにはkeyringが必要で、mysqldを起動させないといけない。
mysqldを止めずに実行する前提なので、バイナリログの最後のイベント(今まさに書き込んでいるイベント)は壊れているかもしれない。ただmysqlbinlogでデコードする時にワーニングが出るだけなのでわかっていさえすれば大丈夫。デフォルトではファイルのタイムスタンプが一致すれば次回はスキップされる。その時アクティブだったバイナリログは大体先頭から転送し直しになる。
コマンドの実行間隔がRPOになるが、他にレプリカがあれば最新のデータはそいつから取り出せるのでPITRの文脈であれば数分~15分, 30分, 1時間くらいは許容範囲なのではないかしら。

$ rsync user@host:/var/lib/mysql/mysql-bin.* ./
$ ll
total 375940
-rw-r----- 1 yoku0825 yoku0825       381 Jun 20 09:37 bin.000009
-rw-r----- 1 yoku0825 yoku0825       569 Jun 20 09:37 bin.000010
-rw-r----- 1 yoku0825 yoku0825 269449054 Jun 20 09:38 bin.000011
-rw-r----- 1 yoku0825 yoku0825 115493747 Jun 20 09:38 bin.000012
-rw-r----- 1 yoku0825 yoku0825        52 Jun 20 09:38 bin.index

mysqlbinlog

俺の大好きなパターン。 REPLICATION SLAVE権限が必要。 —read-from-remote-server—rawを組み合わせて使う。 --rawを使わない場合はデコード後のテキストファイルが手に入るが、この場合PITRの時の —stop-datetimeなどのオプションが使えなくなる = テキストエディタで編集しないといけないのでなるべくそのままのバイナリログの形が好み。

  • 日々の覚書: mysqlbinlogでバイナリログをバックアップするとか
  • yoku0825/binlog_stream_container: Docker image for streaming MySQL binlog backup.
    通信経路圧縮をするにはzlibを使った -Cだったが、8.0.18とそれ以降では —compression-algorithmsでzstdが指定可能。zstdの方が軽くて速い。これもbinlog圧縮がされているなら圧縮後の転送量&ファイルサイズで済む。
    rsyncのようなワイルドカード指定がないので1つずつバイナリログファイル名を指定するか、「このバイナリログ以降のすべてをストリームし続ける( = —stop-never )」の2択しかない。転送対象に指定したバイナリログは手元にあろうがなかろうが全部先頭から再転送になる。
    rsyncと違ってmysqldが落ちている間は使えない。基本的にファイルを全部読み切ってから終了(または、 --stop-neverで次のバイナリログを待機)するので最後のイベントが無効ってことはない。mysqlbinlog側がプロセスダウンしない限りは。
    binlog暗号化されている場合、mysqlbinlogが保管したファイルは平文に戻っている。得られたバイナリログを別途暗号化するなりなんなりが必要。
    --stop-neverを使っている場合はmysqlbinlogプロセスが常駐して ( レプリケーションのレプリカと同じように ) バイナリログを逐次受信するためRPOがすこぶる短いことが期待できる。プロセスダウンをちゃんと検知して必要なら再起動する必要はあるけれども。
    そのへん(プロセスが起動された時に手元の最後のバイナリログのみ先頭から取り直す、手元にバイナリログがないならサーバーにある最初のファイルから読み出す)をケアしたスクリプトがこんな感じ。
  • https://github.com/yoku0825/binlog_stream_container/blob/main/binlog_stream_wrapper.sh#L22-L53

( ´-`).oO( holdedってなんだ… heldだろう…

$ export MYSQL_PWD=xx
$ for num in {9..12} ; do
> mysqlbinlog -h sandbox.oci.yoku0825.work -P 64080 --raw -ubinlog -R --compression-algorithms=zstd "$(printf "%06d" $num)"> done

$ ll
total 375936
-rw-r----- 1 yoku0825 yoku0825       381 Jun 20 09:40 bin.000009
-rw-r----- 1 yoku0825 yoku0825       569 Jun 20 09:40 bin.000010
-rw-r----- 1 yoku0825 yoku0825 269449054 Jun 20 09:40 bin.000011
-rw-r----- 1 yoku0825 yoku0825 115493747 Jun 20 09:41 bin.000012

MySQL Shellのutil.dumpBinlogs

最近の新顔。MySQL Shell 9.2か何かの新機能で、 MySQL Shell 8.4には入っていない。2025年6月現在ではInnovation ReleaseのMySQL Shellが必要。
対象になるバイナリログファイルを特定するために SHOW BINARY LOGSを実行するので REPLICATION CLIENT権限、実際にバイナリログを吸い上げるために REPLICATION SLAVE権限が必要。MySQLプロトコルを使うので mysqldがダウンしてるとダメ。
俺が詳しくないだけかも知れないけど通信経路の圧縮はなさそう? サーバー側のbinlog圧縮が効いてれば節約になるのは他のと同じ。コイツだけ、「転送してきたバイナリログを圧縮してファイルに落とす」ので別途の圧縮は不要( binlog_transaction_compression
初回のみ、「最初に取り始めるバイナリログファイル名」を指定する必要があるが、2回目以降は「初回のバックアップで読み切ったポジションをメタデータファイルに残す」ので勝手に差分取得が可能。

mysqlbinlogと違って常駐はできないのでrsyncと同じくRPOの制約がある。実行のたびに指定したディレクトリに日時を示したディレクトリができるっぽい。

$ mysqlsh -h sandbox.oci.yoku0825.work -P 64080 -ubinlog -- util dumpBinlogs ./ { --startFrom=bin.000009 }
$ ll
total 8
drwxr-x--- 2 yoku0825 yoku0825 4096 Jun 20 09:58 2025-06-20-00-56-58
-rw-r----- 1 yoku0825 yoku0825  457 Jun 20 09:56 @.binlog.info.json

$ ll
total 8
drwxr-x--- 2 yoku0825 yoku0825 4096 Jun 20 09:58 2025-06-20-00-56-58
-rw-r----- 1 yoku0825 yoku0825  457 Jun 20 09:56 @.binlog.info.json

$ ll 2025-06-20-00-56-58/
total 182440
-rw-r----- 1 yoku0825 yoku0825       251 Jun 20 09:58 @.binlog.done.json
-rw-r----- 1 yoku0825 yoku0825     31614 Jun 20 09:56 @.binlog.json
-rw-r----- 1 yoku0825 yoku0825       275 Jun 20 09:56 bin.000009.json
-rw-r----- 1 yoku0825 yoku0825       274 Jun 20 09:56 bin.000009.zst
-rw-r----- 1 yoku0825 yoku0825       278 Jun 20 09:56 bin.000010.json
-rw-r----- 1 yoku0825 yoku0825       342 Jun 20 09:56 bin.000010.zst
-rw-r----- 1 yoku0825 yoku0825       295 Jun 20 09:58 bin.000011.json
-rw-r----- 1 yoku0825 yoku0825 130723963 Jun 20 09:58 bin.000011.zst
-rw-r----- 1 yoku0825 yoku0825       324 Jun 20 09:57 bin.000012.json
-rw-r----- 1 yoku0825 yoku0825  56018567 Jun 20 09:57 bin.000012.zst

もう一度実行すると差分がまた日付のディレクトリに現れる。今度は startFromの指定をする必要はない。バックアップ先ディレクトリで一度でもdumpBinlogsが実行されていれば勝手に途中から実行する。

$ mysqlsh -h sandbox.oci.yoku0825.work -P 64080 -ubinlog -- util dumpBinlogs ./
Starting from previous dump: /home/yoku0825/work/2025-06-20-00-56-58, created at: 2025-06-20 00:56:58 UTC
Starting from binary log file: bin.000012:115493970
Will finish at binary log file: bin.000012:121584248
Dumping 1 binlogs (6.09 MB of data) using 4 threads
100% (6.09 MB / 6.09 MB), 3.27 MB/s, 2.46 MB/s compressed, 1 / 1 binlogs done
Dump was written to: /home/yoku0825/work/2025-06-20-01-03-00
Total duration: 00:00:01s
Binlogs dumped: 1
GTID set dumped: 6f9ee6f0-1feb-11f0-a8f1-02001702f486:449-9081
Uncompressed data size: 6.09 MB
Compressed data size: 4.64 MB
Compression ratio: 1.3
Events written: 17267
Bytes written: 4.64 MB
Average uncompressed throughput: 3.35 MB/s
Average compressed throughput: 2.55 MB/s

$ ll
total 16
drwxr-x--- 2 yoku0825 yoku0825 4096 Jun 20 09:58 2025-06-20-00-56-58
drwxr-x--- 2 yoku0825 yoku0825 4096 Jun 20 10:03 2025-06-20-01-03-00
-rw-r----- 1 yoku0825 yoku0825  457 Jun 20 09:56 @.binlog.info.json

$ ll 2025-06-20-01-03-00/
total 4568
-rw-r----- 1 yoku0825 yoku0825      42 Jun 20 10:03 @.binlog.done.json
-rw-r----- 1 yoku0825 yoku0825   31480 Jun 20 10:03 @.binlog.json
-rw-r----- 1 yoku0825 yoku0825     355 Jun 20 10:03 bin.000012.json
-rw-r----- 1 yoku0825 yoku0825 4636447 Jun 20 10:03 bin.000012.zst

$ ll */bin.000012*.zst
-rw-r----- 1 yoku0825 yoku0825 56018567 Jun 20 09:57 2025-06-20-00-56-58/bin.000012.zst
-rw-r----- 1 yoku0825 yoku0825  4636447 Jun 20 10:03 2025-06-20-01-03-00/bin.000012.zst

ファイルも先頭から取り直しているわけではなかった。

各zstファイルを解凍するといつもの(デコード前の)バイナリログが出てくる。
util.dumpBinlogsで取ったバックアップは util.loadBinlogsでディレクトリを指定する形で一気に適用できる。一つ一つ解凍して mysqlbinlog | mysqlとかする必要はない。

binlog暗号化に関する話は mysqlbinlogと一緒。binlog圧縮の解凍はクライアントの責務だけど、暗号化されたbinlogを適用可能に復号するのはMySQLサーバー側の責務なので、MySQLプロトコルを使うクライアントサーバー形式だとどうしてもこうなる ( なので gh-ostとかも一緒)

意外と楽しかった。

最新のMySQL ShellだけでMySQLのPITR可能なバックアップを設定する(フルバックアップ編)

$
0
0

MySQL Shellには util.dumpInstance = フルバックアップと、9.2くらいから util.dumpBinlogs = 増分バックアップがあるので、ひょっとしたらMySQL ShellだけでPITR可能なバックアップをシンプルに設定できるのではないかと思った。

TL;DR

  • util.dumpBinlogsは「前回吸い上げたバイナリログから増えたぶんだけをMySQLプロトコルで吸い上げてローカルFSに保管」することができる。初回のみ最初のバイナリログファイル名の指定が必要
    • mysqlbinlog --stop-never --raw -Rではプロセスが再起動する時に常に自分でコピーを開始するバイナリログファイル名を指定しなければならなかった
  • util.dumpBinlogsgtid_mode=ONでしか使えない
    • リストア用である util.loadBinlogsは終端を示す stopAfterまたは stopBeforeにGTIDしか使えない
      • mysqlbinlog --stop-datetime=..が使えないのでPITRと考えると時間とGTIDのマップを自分でやらないといけない
      • じゃあ時間とGTIDを定期的に記録すればいけるんじゃないかなと思った

長くなったので今回はフルバックアップのみ。


大前提としてMySQL側は gtid_mode=ONでないといけない。あと、 util.dumpBinlogsはMySQL Shell 9.2だかそこいらからの機能なので今現在で 最新のMySQL Shell 9.3にしておく。

sudo dnf install -y https://dev.mysql.com/get/Downloads/MySQL-Shell/mysql-shell-9.3.0-1.el8.x86_64.rpm

リストアターゲットにするMySQLはdockerコンテナで済ます。
--restart=on-failureRESTARTステートメントに必要だから(今回使わないけどクセにしている)。
-eのいくつかはパスワードレスでネットワークがつながっている限りどこからでもrootでログインできるようにするもの。
URLが指定してあるのは MySQL公式のDockerリポジトリがcontainer-registry.oracle.comに引っ越していたから。
イメージの指定のあとに続いているのはそのまま mysqldに渡されるオプションになる。ここで --gtid-mode=ON --enforce-gtid-consistency=ONにしてGTIDを有効にしている。

docker run -d -P \
  --restart=on-failure \
  -e MYSQL_ALLOW_EMPTY_PASSWORD=1 \
  -e MYSQL_ROOT_PASSWORD="""" \
  -e MYSQL_ROOT_HOST=""%"" \
  container-registry.oracle.com/mysql/community-server:8.4.5 \
    --gtid-mode=ON --enforce-gtid-consistency=ON --lower-case-table-names

mysql> CREATE USER mysqlsh_backup IDENTIFIED BY 'secret_password';
Query OK, 0 rows affected (0.02 sec)

mysql> GRANT SELECT, RELOAD, REPLICATION SLAVE, REPLICATION CLIENT, EVENT, TRIGGER, BACKUP_ADMIN ON *.* TO mysqlsh_backup;
Query OK, 0 rows affected (0.01 sec)

mysql> SHOW GRANTS FOR mysqlsh_backup;
+------------------------------------------------------------------------------------------------------------+
| Grants for mysqlsh_backup@%                                                                                |
+------------------------------------------------------------------------------------------------------------+
| GRANT SELECT, RELOAD, REPLICATION SLAVE, REPLICATION CLIENT, EVENT, TRIGGER ON *.* TO `mysqlsh_backup`@`%` |
| GRANT BACKUP_ADMIN ON *.* TO `mysqlsh_backup`@`%`                                                          |
+------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

MySQL Shellは mysql_config_editorで作ったクレデンシャルを食えるので、先に設定しておく。

$ mysql_config_editor set --login-path=backup --user=mysqlsh_backup --password
Enter password:     ### 対話的に入れるしかない

$ mysqlsh --login-path=backup -h 172.17.0.2 -e "SHOW GRANTS"
Grants for mysqlsh_backup@%
GRANT SELECT, RELOAD, REPLICATION CLIENT, EVENT, TRIGGER ON *.* TO `mysqlsh_backup`@`%`
GRANT BACKUP_ADMIN ON *.* TO `mysqlsh_backup`@`%`

GTIDとPITRの時刻を紐づけるためにハートビートテーブルを作って定期的に実行する。

#!/usr/bin/perl

use strict;
use warnings;
use utf8;

use Time::Piece;
use DBI;

my $dsn = "dbi:mysql:;host=172.17.0.2";
my $user= "root";
my $pass= "";
my $opt = { RaiseError => 1, PrintError => 1, use_mysql_utf8 => 1, mysql_connect_timeout => 1 };

my $conn= DBI->connect($dsn, $user, $pass, $opt) or die;

$conn->do("CREATE DATABASE IF NOT EXISTS sakura");
$conn->do("CREATE TABLE IF NOT EXISTS sakura.hb (seq SERIAL, hostname VARCHAR(255) NOT NULL, recorded DATETIME NOT NULL, gtid_executed TEXT NOT NULL)");

while ()
{
  my $now= Time::Piece::localtime;
  $conn->do(q|INSERT INTO sakura.hb (hostname, recorded, gtid_executed) VALUES (@@hostname, ?, @@global.gtid_executed)|, undef, $now->strftime("%Y-%m-%d %H:%M:%S"));
  printf("%s\n", $now->strftime("%Y-%m-%d %H:%M:%S"));
  sleep 1;
}

まずフルバックアップ。MySQL Shellはいくつからだったかデフォルトモードが --jsから --sqlに変わっているので util.*系を呼び出す時には --jsが必要。

$ mysqlsh --login-path=backup -h 172.17.0.2 --js -- util dumpInstance /path/to/full_backup

-e "util.dumpInstance('/path/to/full_backup')"でも取れると思うけど俺は --記法の方が好き(単に先にそっちでおぼえたから)

$ ll /path/to/full_backup/
total 86260
-rw-r-----. 1 yoku0825 yoku0825      599 Jul  2 02:26 @.done.json
-rw-r-----. 1 yoku0825 yoku0825    31733 Jul  2 02:26 @.json
-rw-r-----. 1 yoku0825 yoku0825      238 Jul  2 02:26 @.post.sql
-rw-r-----. 1 yoku0825 yoku0825      238 Jul  2 02:26 @.sql
-rw-r-----. 1 yoku0825 yoku0825     4708 Jul  2 02:26 @.users.sql
-rw-r-----. 1 yoku0825 yoku0825      386 Jul  2 02:26 sakura.json
-rw-r-----. 1 yoku0825 yoku0825      807 Jul  2 02:26 sakura.sql
-rw-r-----. 1 yoku0825 yoku0825      639 Jul  2 02:26 sakura@hb.json
-rw-r-----. 1 yoku0825 yoku0825      830 Jul  2 02:26 sakura@hb.sql
-rw-r-----. 1 yoku0825 yoku0825      318 Jul  2 02:26 sakura@hb@@0.tsv.zst
-rw-r-----. 1 yoku0825 yoku0825        8 Jul  2 02:26 sakura@hb@@0.tsv.zst.idx
-rw-r-----. 1 yoku0825 yoku0825      401 Jul  2 02:26 sbtest.json
-rw-r-----. 1 yoku0825 yoku0825      807 Jul  2 02:26 sbtest.sql
-rw-r-----. 1 yoku0825 yoku0825      637 Jul  2 02:26 sbtest@sbtest1.json
-rw-r-----. 1 yoku0825 yoku0825      856 Jul  2 02:26 sbtest@sbtest1.sql
-rw-r-----. 1 yoku0825 yoku0825 29377820 Jul  2 02:26 sbtest@sbtest1@0.tsv.zst
-rw-r-----. 1 yoku0825 yoku0825      496 Jul  2 02:26 sbtest@sbtest1@0.tsv.zst.idx
-rw-r-----. 1 yoku0825 yoku0825 29415751 Jul  2 02:26 sbtest@sbtest1@1.tsv.zst
-rw-r-----. 1 yoku0825 yoku0825      496 Jul  2 02:26 sbtest@sbtest1@1.tsv.zst.idx
-rw-r-----. 1 yoku0825 yoku0825 29415671 Jul  2 02:26 sbtest@sbtest1@2.tsv.zst
-rw-r-----. 1 yoku0825 yoku0825      496 Jul  2 02:26 sbtest@sbtest1@2.tsv.zst.idx
-rw-r-----. 1 yoku0825 yoku0825      117 Jul  2 02:26 sbtest@sbtest1@@3.tsv.zst
-rw-r-----. 1 yoku0825 yoku0825        8 Jul  2 02:26 sbtest@sbtest1@@3.tsv.zst.idx

$ jq . /path/to/full_backup/@.json
{
    "dumper": "mysqlsh Ver 9.3.0 for Linux on x86_64 - for MySQL 9.3.0 (MySQL Community Server (GPL))",
    "version": "2.0.1",
    "origin": "dumpInstance",
    "schemas": [
        "sakura",
        "sbtest"
    ],
    "basenames": {
        "sakura": "sakura",
        "sbtest": "sbtest"
    },
    "users": [
        "'healthchecker'@'localhost'",
        "'mysqlsh_backup'@'%'",
        "'root'@'%'",
        "'root'@'localhost'"
    ],
    "defaultCharacterSet": "utf8mb4",
    "tzUtc": true,
    "bytesPerChunk": 64000000,
    "user": "mysqlsh_backup",
    "hostname": "yoku0825-oci",
    "gtidExecutedInconsistent": false,
    "consistent": true,
    "targetVersion": "9.3.0",
    "hasLibraryDdl": false,
    "source": {
        "binlog": {
            "file": "binlog.000002",
            "position": 190942962,
            "gtidExecuted": "3b5d02fb-56e7-11f0-8f01-0242ac110002:1-455"
        },
        "sysvars": {
..
        },
        "topology": {
            "canonicalAddress": "a7786309934c:3306"
        }
    },
    "server": "a7786309934c",
    "serverVersion": "8.4.5",
    "binlogFile": "binlog.000002",
    "binlogPosition": 190942962,
    "gtidExecuted": "3b5d02fb-56e7-11f0-8f01-0242ac110002:1-455",
    "partialRevokes": false,
    "compatibilityOptions": [],
    "capabilities": [],
    "checksum": false,
    "begin": "2025-07-02 02:26:38"
}


$ jq -r .begin /path/to/full_backup/@.json
2025-07-02 02:26:38

バックアッププロセス自体の情報は @.json@.post.jsonに含まれている。論理バックアップである util.dumpInstanceのデータの切断面は開始時刻になると期待するので、 @.jsonに含まれる beginを確認しておく。

リストア用にもう1つコンテナを立ち上げて、 util.loadDumpに必要な local_infileをONにして実行。

docker run -d -P \
  --restart=on-failure \
  -e MYSQL_ALLOW_EMPTY_PASSWORD=1 \
  -e MYSQL_ROOT_PASSWORD="""" \
  -e MYSQL_ROOT_HOST=""%"" \
  container-registry.oracle.com/mysql/community-server:8.4.5 \
    --gtid-mode=ON --enforce-gtid-consistency=ON --lower-case-table-names

mysql> SET GLOBAL local_infile = 1;
Query OK, 0 rows affected (0.00 sec)

$ mysqlsh root@172.17.0.3 --js -- util loadDump /path/to/full_backup/ { --loadUsers=true --ignoreExistingObjects=true --updateGtidSet=replace }   ### デフォルトではユーザーまでは生成されないのと、既存のアカウントは無視してくれればいい、gtid_executedは上書きする

狙った通りにリストアできてるか、ハートビートテーブルから確認できる。
綺麗にぴったり  @.json の begin と一致してよかった。

mysql> SELECT * FROM sakura.hb ORDER BY seq DESC LIMIT 1;
+-----+--------------+---------------------+--------------------------------------------+
| seq | hostname     | recorded            | gtid_executed                              |
+-----+--------------+---------------------+--------------------------------------------+
|  65 | a7786309934c | 2025-07-02 02:26:38 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-454 |
+-----+--------------+---------------------+--------------------------------------------+
1 row in set (0.00 sec)

特に仕組みを作っている最中はこのハートビートテーブルが役に立つというのが一番の知見かもしれない。

増分バックアップ編に続く。

最新のMySQL ShellだけでMySQLのPITR可能なバックアップを設定する(増分バックアップ編)

$
0
0

日々の覚書: 最新のMySQL ShellだけでMySQLのPITR可能なバックアップを設定する(フルバックアップ編)の続き。

最初の1回は必ず sinceまたは startFromの指定が必要。

$ mysqlsh --login-path=backup -h 172.17.0.2 --js -- util dumpBinlogs /path/to/binlog_backup
ERROR: ArgumentError: One of the 'since' or 'startFrom' options must be set because the destination directory '/path/to/binlog_backup' does not contain any dumps yet.

startFrommysqlbinlog -Rと同じようにバイナリログファイル名を指定する ( CHANGE REPLICATION SOURCE TOと同じ仕組みなので、ファイルの絶対パスではなくて SHOW BINARY LOGSで出てくるファイル名を指定する必要があるアレ )

sinceは過去に実行した util.dumpInstance()util.dumpBinlogs()のディレクトリを指定するとそれ以降のバイナリログを吸い上げてくれる。

2回目以降は「バックアップ先に指定した /path/to/binlog_backupsinceに渡されたと思ってそれ以降のバイナリログだけを増分でバックアップしてくれる。

$ mysqlsh --login-path=backup -h 172.17.0.2 --js -- util dumpBinlogs /path/to/binlog_backup --since=/path/to/full_backup
Starting from previous dump: /path/to/full_backup, created at: 2025-07-02 02:26:38 UTC
Starting from binary log file: binlog.000002:190942962
Will finish at binary log file: binlog.000002:195333734
Dumping 1 binlogs (4.39 MB of data) using 4 threads
100% (4.39 MB / 4.39 MB), 0.00 B/s, 0.00 B/s compressed, 1 / 1 binlogs done
Dump was written to: /path/to/binlog_backup/2025-07-02-05-50-55
Total duration: 00:00:00s
Binlogs dumped: 1
GTID set dumped: 3b5d02fb-56e7-11f0-8f01-0242ac110002:456-12680
Uncompressed data size: 4.39 MB
Compressed data size: 681.10 KB
Compression ratio: 6.4
Events written: 61123
Bytes written: 681.10 KB
Average uncompressed throughput: 4.39 MB/s
Average compressed throughput: 681.10 KB/s

$ ll /path/to/binlog_backup/ -R
/path/to/binlog_backup/:
total 4
drwxr-x---. 2 yoku0825 yoku0825 104 Jul  2 05:50 2025-07-02-05-50-55
-rw-r-----. 1 yoku0825 yoku0825 454 Jul  2 05:50 @.binlog.info.json

/path/to/binlog_backup/2025-07-02-05-50-55:
total 708
-rw-r-----. 1 yoku0825 yoku0825     42 Jul  2 05:50 @.binlog.done.json
-rw-r-----. 1 yoku0825 yoku0825  31443 Jul  2 05:50 @.binlog.json
-rw-r-----. 1 yoku0825 yoku0825    358 Jul  2 05:50 binlog.000002.json
-rw-r-----. 1 yoku0825 yoku0825 681099 Jul  2 05:50 binlog.000002.zst

というわけで、 util.dumpInstanceで取ったフルバックアップのディレクトリを指定すると「そのバックアップをフルリストアして以降に生成されたバイナリログだけ」が手に入る。

更には sinceなしで実行すると(というかバックアップ先に一度でもダンプしたことがあると sinceオプションは拒否される)、 /path/to/binlog_backupからの増分だけを取ってくれるので

$ mysqlsh --login-path=backup -h 172.17.0.2 --js -- util dumpBinlogs /path/to/binlog_backup
Starting from previous dump: /path/to/binlog_backup/2025-07-02-05-50-55, created at: 2025-07-02 05:50:55 UTC
Starting from binary log file: binlog.000002:195333734
Will finish at binary log file: binlog.000002:212891636
Dumping 1 binlogs (17.56 MB of data) using 4 threads
100% (17.56 MB / 17.56 MB), 0.00 B/s, 0.00 B/s compressed, 1 / 1 binlogs done
Dump was written to: /path/to/binlog_backup/2025-07-02-05-53-16
Total duration: 00:00:00s
Binlogs dumped: 1
GTID set dumped: 3b5d02fb-56e7-11f0-8f01-0242ac110002:12681-22848
Uncompressed data size: 17.56 MB
Compressed data size: 5.76 MB
Compression ratio: 3.1
Events written: 111003
Bytes written: 5.76 MB
Average uncompressed throughput: 17.56 MB/s
Average compressed throughput: 5.76 MB/s

$ ll /path/to/binlog_backup/ -R
/path/to/binlog_backup/:
total 4
drwxr-x---. 2 yoku0825 yoku0825 104 Jul  2 05:50 2025-07-02-05-50-55
drwxr-x---. 2 yoku0825 yoku0825 104 Jul  2 05:53 2025-07-02-05-53-16
-rw-r-----. 1 yoku0825 yoku0825 454 Jul  2 05:50 @.binlog.info.json

/path/to/binlog_backup/2025-07-02-05-50-55:
total 708
-rw-r-----. 1 yoku0825 yoku0825     42 Jul  2 05:50 @.binlog.done.json
-rw-r-----. 1 yoku0825 yoku0825  31443 Jul  2 05:50 @.binlog.json
-rw-r-----. 1 yoku0825 yoku0825    358 Jul  2 05:50 binlog.000002.json
-rw-r-----. 1 yoku0825 yoku0825 681099 Jul  2 05:50 binlog.000002.zst

/path/to/binlog_backup/2025-07-02-05-53-16:
total 5664
-rw-r-----. 1 yoku0825 yoku0825      42 Jul  2 05:53 @.binlog.done.json
-rw-r-----. 1 yoku0825 yoku0825   31383 Jul  2 05:53 @.binlog.json
-rw-r-----. 1 yoku0825 yoku0825     363 Jul  2 05:53 binlog.000002.json
-rw-r-----. 1 yoku0825 yoku0825 5756607 Jul  2 05:53 binlog.000002.zst

こうなる。
ファイルが散らばるのは玉に瑕だけどまあ仕方ない(本当はアーカイブにしてgpgでも使ってファイルを暗号化したい…)

ちなみに別のMySQLだけど、 util.dumpBinlogsはちゃんと並列化されていて速い。

2025-07-03T02:17:32.216078-00:00        89296 Connect   root@localhost on  using Socket
2025-07-03T02:17:32.450935-00:00        89296 Query     SELECT @@SESSION.session_track_system_variables
2025-07-03T02:17:32.451152-00:00        89296 Query     SET SESSION session_track_system_variables = 'time_zone,autocommit,character_set_client,character_set_results,character_set_connection, sql_mode'
2025-07-03T02:17:32.451629-00:00        89296 Query     show GLOBAL variables where `variable_name` in ('log_bin')
2025-07-03T02:17:32.452602-00:00        89296 Query     show GLOBAL variables where `variable_name` in ('gtid_mode')
2025-07-03T02:17:32.453317-00:00        89296 Query     SELECT @@GLOBAL.VERSION
2025-07-03T02:17:32.453394-00:00        89296 Query     SHOW GLOBAL VARIABLES
2025-07-03T02:17:32.454547-00:00        89296 Query     SHOW MASTER STATUS
2025-07-03T02:17:32.454649-00:00        89296 Query     SELECT COALESCE(@@report_host, @@hostname),  COALESCE(@@report_port, @@port)
2025-07-03T02:17:32.454741-00:00        89296 Query     show GLOBAL variables where `variable_name` in ('group_replication_group_name')
2025-07-03T02:17:32.455317-00:00        89296 Query     show GLOBAL variables where `variable_name` in ('group_replication_view_change_uuid')
2025-07-03T02:17:32.455898-00:00        89296 Query     SHOW SCHEMAS LIKE 'mysql_innodb_cluster_metadata'
2025-07-03T02:17:32.456951-00:00        89296 Query     SHOW BINARY LOGS
2025-07-03T02:17:32.458562-00:00        89297 Connect   root@localhost on  using Socket
2025-07-03T02:17:32.458579-00:00        89298 Connect   root@localhost on  using Socket
2025-07-03T02:17:32.458762-00:00        89298 Query     SELECT VERSION()
2025-07-03T02:17:32.458860-00:00        89299 Connect   root@localhost on  using Socket
2025-07-03T02:17:32.458883-00:00        89298 Query     SET @master_binlog_checksum = 'NONE', @source_binlog_checksum = 'NONE'
2025-07-03T02:17:32.458888-00:00        89297 Query     SELECT VERSION()
2025-07-03T02:17:32.458963-00:00        89298 Binlog Dump GTID  Log: 'bin.000011' Pos: 4 GTIDs: ''
2025-07-03T02:17:32.459024-00:00        89297 Query     SET @master_binlog_checksum = 'NONE', @source_binlog_checksum = 'NONE'
2025-07-03T02:17:32.459089-00:00        89299 Query     SELECT VERSION()
2025-07-03T02:17:32.459107-00:00        89297 Binlog Dump GTID  Log: 'bin.000012' Pos: 4 GTIDs: ''
2025-07-03T02:17:32.459240-00:00        89299 Query     SET @master_binlog_checksum = 'NONE', @source_binlog_checksum = 'NONE'
2025-07-03T02:17:32.459305-00:00        89299 Binlog Dump GTID  Log: 'bin.000013' Pos: 4 GTIDs: ''
2025-07-03T02:17:35.289987-00:00        89296 Quit

util.dumpBinlogsを定期的に繰り返して十分バイナリログが貯まった段階で、 前回作っておいたハートビートテーブルからPITR対象になるGTIDを検索する。

mysql> SELECT * FROM sakura.hb WHERE recorded = '2025-07-03 00:00:00';
+-------+--------------+---------------------+----------------------------------------------+
| seq   | hostname     | recorded            | gtid_executed                                |
+-------+--------------+---------------------+----------------------------------------------+
| 77470 | a7786309934c | 2025-07-03 00:00:00 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887 |
+-------+--------------+---------------------+----------------------------------------------+
1 row in set (0.04 sec)

util.loadBinlogsstopBefore, stopAfterどちらもGTIDしか取れないので、ハートビートテーブルでGTIDを保管しておかないとたぶん割と大変。
/path/to/binlog_backup/*を覗くとわかるけれど、それぞれのバイナリログファイルは「バイナリログの途中からでもちゃんと差分で取得」されているし、「zstdで圧縮」もされているので一つずつ展開してそれっぽい時間を当たって GTID_NEXTを得るのは大変…。

### リストア用のまっさらなコンテナ
docker run -d -P \
  --restart=on-failure \
  -e MYSQL_ALLOW_EMPTY_PASSWORD=1 \
  -e MYSQL_ROOT_PASSWORD="""" \
  -e MYSQL_ROOT_HOST=""%"" \
  container-registry.oracle.com/mysql/community-server:8.4.5 \
    --gtid-mode=ON --enforce-gtid-consistency=ON --lower-case-table-names

### フルリストア
$ mysql -h172.17.0.3 -uroot -e "SET GLOBAL local_infile=1"
$ mysqlsh root@172.17.0.3 --js -- util loadDump /path/to/full_backup/ { --loadUsers=true --ignoreExistingObjects=true --updateGtidSet=replace }   ### デフォルトではユーザーまでは生成されないのと、既存のアカウントは無視してくれればいい、gtid_executedは上書きする

### 昨日取ったフルバックアップの地点
$ mysql -h172.17.0.3 -uroot -e "SELECT * FROM sakura.hb ORDER BY seq DESC LIMIT 1"
+-----+--------------+---------------------+--------------------------------------------+
| seq | hostname     | recorded            | gtid_executed                              |
+-----+--------------+---------------------+--------------------------------------------+
|  65 | a7786309934c | 2025-07-02 02:26:38 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-454 |
+-----+--------------+---------------------+--------------------------------------------+

### mysqlスキーマが書き換わってる(はずな)ので一応RESTARTを挟む。
$ mysql -h172.17.0.3 -uroot -e "RESTART"

$ mysqlsh root@172.17.0.3 --js -- util loadBinlogs /path/to/binlog_backup { --stopAfter="3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887" }
ERROR: ArgumentError: Argument #2: Invalid value of the 'stopAfter' option, expected a GTID in format: source_id:[tag:]:transaction_id

げ、 stopAfterは単発のGTIDじゃないといけなくて gtid_executedではダメなのか…。これだと複数の server_uuidが混じった時に「どれが最後にインクリメントされたGTIDなのか」を探すのが非常に面倒な気がする…。

$ mysqlsh root@172.17.0.3 --js -- util loadBinlogs /path/to/binlog_backup { --stopAfter="3b5d02fb-56e7-11f0-8f01-0242ac110002:87887" }
Please provide the password for 'root@172.17.0.3':
Save password for 'root@172.17.0.3'? [Y]es/[N]o/Ne[v]er (default No):
Loading 1 binlogs, 50.61 MB of data
Opening dump '/path/to/binlog_backup'
  Loading dump '2025-07-03-02-20-45' created at 2025-07-03 02:20:45 UTC
    Loading binary log file 'binlog.000002', GTID set: 3b5d02fb-56e7-11f0-8f01-0242ac110002:456-97567 (50.61 MB)
      Found starting GTID: 3b5d02fb-56e7-11f0-8f01-0242ac110002:456
      Stopped after GTID: 3b5d02fb-56e7-11f0-8f01-0242ac110002:87887
89% (45.37 MB / 50.61 MB), 0.00 B/s, 0.00 B/s compressed, 3.59K stmts/s, 1 / 1 binlogs done
Total duration: 00:03:52s
Binlogs loaded: 1
Uncompressed data size: 50.61 MB
Compressed data size: 11.26 MB
Statements executed: 816983
Average uncompressed throughput: 194.95 KB/s
Average compressed throughput: 43.31 KB/s
Average statement throughput: 3.51 KB/s

一応それっぽい時間にはなったけど… stopAfter=3b5d02fb-56e7-11f0-8f01-0242ac110002:87887なのにその1つ手前で止まるのは合っているのか。。

$ mysql -h172.17.0.3 -uroot -e "SELECT * FROM sakura.hb ORDER BY seq DESC LIMIT 1"
+-------+--------------+---------------------+----------------------------------------------+
| seq   | hostname     | recorded            | gtid_executed                                |
+-------+--------------+---------------------+----------------------------------------------+
| 77469 | a7786309934c | 2025-07-02 23:59:59 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87886 |
+-------+--------------+---------------------+----------------------------------------------+

【2025/07/04 10:24】

ハートビートテーブルのgtid_executedの方がむしろ合ってなかった。

$  mysql -h172.17.0.4 -uroot -e "SHOW BINARY LOG STATUS"
+---------------+----------+--------------+------------------+-----------------------------------------------------------------------------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                       |
+---------------+----------+--------------+------------------+-----------------------------------------------------------------------------------------+
| binlog.000003 | 45729885 |              |                  | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
bf458fdd-586d-11f0-99ef-0242ac110004:1-18 |
+---------------+----------+--------------+------------------+-----------------------------------------------------------------------------------------+



たぶんまだ続く(スイッチオーバーした後にgtid_executedが複数になるケースを試してから)

最新のMySQL ShellだけでMySQLのPITR可能なバックアップを設定する(実践編)

$
0
0

日々の覚書: 最新のMySQL ShellだけでMySQLのPITR可能なバックアップを設定する(増分バックアップ編)の更に続き。

フェイルオーバー、ソース切り替えが起こって gtid_executedが複数行になった時にどうなるのかを調べる。

昨日リストアした172.17.0.3に更にsysbenchでちょっと更新をかけながらハートビートテーブルにも書かせていた。


mysql> SHOW BINARY LOG STATUS;

+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+

| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                         |

+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+

| binlog.000003 | 48101470 |              |                  | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,

8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1686 |

+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+

1 row in set (0.00 sec)

切り替わりが発生すると、それまで積み重ねてきたバイナリログのバックアップに追加することはできなくなる。おそらく切り替わったらフルバックアップを取り直してそこからまたバイナリログを積み上げるのが良さそう。

$ mysqlsh -h 172.17.0.3 -uroot --no-password --js -- util dumpBinlogs /path/to/binlog_backup
WARNING: Using a password on the command line interface can be insecure.
ERROR: ArgumentError: The source instance is different from the one used in the previous dump and it's not part of an InnoDB Cluster group.

gtid_mode=ONなら切り替わりをまたぐPITRでも他のノードで log_replica_updatesによって十分なバイナリログが残っていれば(実行済みの gtid_nextはスキップされるのでGTIDに隙間ができないように余裕をもって指定すれば良いだけで、完全にGTIDの切れ目で一致させる必要はない)楽なんだけれども、今検証しているこれの範疇からは外れそう。

$ mysqlsh -h 172.17.0.3 -uroot --no-password --js -- util dumpInstance /path/to/another_full_backup/
WARNING: Using a password on the command line interface can be insecure.
Acquiring global read lock
Global read lock acquired
Initializing - done
2 out of 6 schemas will be dumped and within them 2 tables, 0 views.
4 out of 7 users will be dumped.
Gathering information - done
All transactions have been started
Locking instance for backup
Global read lock has been released
Writing global DDL files
Writing users DDL
Running data dump using 4 threads.
NOTE: Progress information uses estimated values and may not be accurate.
Writing schema metadata - done
Writing DDL - done
Writing table metadata - done
Starting data dump
101% (1.08M rows / ~1.06M rows), 536.44K rows/s, 99.41 MB/s uncompressed, 44.03 MB/s compressed
Dump duration: 00:00:02s
Total duration: 00:00:02s
Schemas dumped: 2
Tables dumped: 2
Uncompressed data size: 200.43 MB
Compressed data size: 88.74 MB
Compression ratio: 2.3
Rows written: 1077870
Bytes written: 88.74 MB
Average uncompressed throughput: 95.47 MB/s
Average compressed throughput: 42.27 MB/s

$ mysqlsh -h 172.17.0.3 -uroot --no-password --js -- util dumpBinlogs /path/to/another_binlog { --since=/path/to/another_full_backup/ }
WARNING: Using a password on the command line interface can be insecure.
Starting from previous dump: /path/to/another_full_backup, created at: 2025-07-04 02:28:23 UTC
Starting from binary log file: binlog.000003:48101470
Will finish at binary log file: binlog.000003:48107225
Dumping 1 binlogs (5.75 KB of data) using 4 threads
102% (5.88 KB / 5.75 KB), 0.00 B/s, 0.00 B/s compressed, 1 / 1 binlogs done
Dump was written to: /path/to/another_binlog/2025-07-04-03-31-48
Total duration: 00:00:00s
Binlogs dumped: 1
GTID set dumped: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1687-1701
Uncompressed data size: 5.88 KB
Compressed data size: 1.39 KB
Compression ratio: 4.2
Events written: 70
Bytes written: 1.39 KB
Average uncompressed throughput: 5.88 KB/s
Average compressed throughput: 1.39 KB/s

話は変わるけど、バイナリログを吸い上げたディレクトリの中の日付のディレクトリの中に、「この util.dumpBinlogsが終わった時点での最後のバイナリログファイル名とポジション、 gtid_executed」がメタデータとして吸い上げられていることに気が付いた。

$ ll binlog/2025-07-04-02-45-04/
total 140
-rw-r-----. 1 yoku0825 yoku0825    42 Jul  4 02:45 @.binlog.done.json
-rw-r-----. 1 yoku0825 yoku0825 47356 Jul  4 02:45 @.binlog.json
-rw-r-----. 1 yoku0825 yoku0825   329 Jul  4 02:45 binary-log.043707.json
-rw-r-----. 1 yoku0825 yoku0825  7116 Jul  4 02:45 binary-log.043707.zst
-rw-r-----. 1 yoku0825 yoku0825   303 Jul  4 02:45 binary-log.043708.json
-rw-r-----. 1 yoku0825 yoku0825 22974 Jul  4 02:45 binary-log.043708.zst
-rw-r-----. 1 yoku0825 yoku0825   303 Jul  4 02:45 binary-log.043709.json
-rw-r-----. 1 yoku0825 yoku0825 23087 Jul  4 02:45 binary-log.043709.zst
-rw-r-----. 1 yoku0825 yoku0825   330 Jul  4 02:45 binary-log.043710.json
-rw-r-----. 1 yoku0825 yoku0825 15741 Jul  4 02:45 binary-log.043710.zst

$ jq .endAt binlog/2025-07-04-02-45-04/@.binlog.json
{
  "file": "binary-log.043710",
  "position": 103420,
  "gtidExecuted": "9330ac0f-e14a-11ef-a07c-0200170681c1:1-2455837"
}

話を戻して、切り替わったソースから取ったバイナリログでPITRできるかの話。

$ mysql -h172.17.0.4 -uroot -e "SET GLOBAL local_infile=1"
$ mysqlsh root@172.17.0.4 --js -- util loadDump /path/to/another_full_backup/ { --loadUsers=true --ignoreExistingObjects=true --updateGtidSet=replace --skipBinlog=true }

$ mysql -h172.17.0.4 -uroot -e "SELECT * FROM sakura.hb ORDER BY seq DESC LIMIT 1"
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| seq   | hostname     | recorded            | gtid_executed                                                                             |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| 77870 | 756eebf85ccb | 2025-07-03 05:50:33 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1685 |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+

$ mysql -h172.17.0.4 -uroot -e "RESTART"

たとえば7/4 03:34(UTC) に着地したい場合は、バックアップ元でこんな風に引くことを 前回やっていたけれど、

$ mysql -h172.17.0.3 -e "SELECT * FROM sakura.hb WHERE recorded = '2025-07-04 03:34:00'"
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| seq   | hostname     | recorded            | gtid_executed                                                                             |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| 78015 | 756eebf85ccb | 2025-07-04 03:34:00 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1832 |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+

$ mysqlsh root@172.17.0.4 --js -- util loadBinlogs /path/to/another_binlog { --stopAfter="3b5d02fb-56e7-11f0-8f01-0242ac110002:87887" }   ### こっちは最初のソース172.17.0.2が持っていたGTIDの末尾
Please provide the password for 'root@172.17.0.4':
Save password for 'root@172.17.0.4'? [Y]es/[N]o/Ne[v]er (default No):
ERROR: ArgumentError: Could not find the final binary log file to be loaded that contains GTID '3b5d02fb-56e7-11f0-8f01-0242ac110002:87887'.

$ mysqlsh root@172.17.0.4 --js -- util loadBinlogs /path/to/another_binlog { --stopAfter="8e7dfcc0-57b7-11f0-a722-0242ac110003:1832" }   ### こっちが直接バックアップを取った172.17.0.3が持っていた(復元したい)GTIDの末尾
Please provide the password for 'root@172.17.0.4':
Save password for 'root@172.17.0.4'? [Y]es/[N]o/Ne[v]er (default No):
Loading 2 binlogs, 112.52 KB of data
Opening dump '/path/to/another_binlog'
  Loading dump '2025-07-04-03-31-48' created at 2025-07-04 03:31:48 UTC
    Loading binary log file 'binlog.000003', GTID set: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1687-1701 (5.88 KB)
      Found starting GTID: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1687
  Loading dump '2025-07-04-03-36-11' created at 2025-07-04 03:36:11 UTC
    Loading binary log file 'binlog.000003', GTID set: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1702-1964 (106.64 KB)
      Stopped after GTID: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1832
56% (63.23 KB / 112.52 KB), 0.00 B/s, 0.00 B/s compressed, 0.00 stmts/s, 2 / 2 binlogs done
Total duration: 00:00:00s
Binlogs loaded: 2
Uncompressed data size: 112.52 KB
Compressed data size: 16.59 KB
Statements executed: 1330
Average uncompressed throughput: 63.23 KB/s
Average compressed throughput: 16.59 KB/s
Average statement throughput: 1.33 KB/s

$ mysql -h172.17.0.4 -uroot -e "SELECT * FROM sakura.hb ORDER BY seq DESC LIMIT 1"
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| seq   | hostname     | recorded            | gtid_executed                                                                             |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| 78014 | 756eebf85ccb | 2025-07-04 03:33:59 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1831 |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+

$ mysql -h172.17.0.4 -uroot -e "SHOW BINARY LOG STATUS"
+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                         |
+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+
| binlog.000002 |    59702 |              |                  | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1832 |
+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+

その時の @@server_uuidの値はバイナリログのディレクトリに残ってるので、アプリケーションとかでパースするならこれを使える気がする。

$ jq . /path/to/another_binlog/@.binlog.info.json
{
  "dumper": "mysqlsh Ver 9.3.0 for Linux on x86_64 - for MySQL 9.3.0 (MySQL Community Server (GPL))",
  "version": "2.0.1",
  "origin": "dumpBinlogs",
  "timestamp": "2025-07-04 03:31:48",
  "source": {
    "version": "8.4.5",
    "hostname": "756eebf85ccb",
    "port": 3306,
    "serverUuid": "8e7dfcc0-57b7-11f0-a722-0242ac110003",
    "topology": {
      "canonicalAddress": "756eebf85ccb:3306"
    }
  }
}

が、あんまり凝ったことはしたくないのでハートビートテーブルから取り出すことにする。

### ターゲットはこれ
mysql> SELECT * FROM sakura.hb WHERE recorded = '2025-07-04 03:34:00';
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| seq   | hostname     | recorded            | gtid_executed                                                                             |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| 78015 | 756eebf85ccb | 2025-07-04 03:34:00 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1832 |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
1 row in set (0.04 sec)

### 少し前の(直近でもいい)レコードを取り出す。単一のGTIDだけが取り出されると話が終わっちゃうのでレンジになるように少し前のレコードにしているだけ
mysql> SELECT * FROM sakura.hb WHERE recorded < '2025-07-04 03:33:00' ORDER BY recorded DESC LIMIT 1;
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| seq   | hostname     | recorded            | gtid_executed                                                                             |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| 77954 | 756eebf85ccb | 2025-07-04 03:32:59 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1771 |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
1 row in set (0.05 sec)

### 両者を比較する
mysql> SELECT GTID_SUBTRACT((SELECT gtid_executed FROM sakura.hb WHERE recorded = '2025-07-04 03:34:00'), (SELECT gtid_executed FROM sakura.hb WHERE recorded < '2025-07-04 03:33:00' ORDER BY recorded DESC LIMIT 1)) AS diff;
+------------------------------------------------+
| diff                                           |
+------------------------------------------------+
| 8e7dfcc0-57b7-11f0-a722-0242ac110003:1772-1832 |
+------------------------------------------------+
1 row in set (0.09 sec)

グループレプリケーションのマルチプライマリーモードやマルチソースレプリケーション、この時間の間にフェイルオーバーが走っていなければここで抽出されるGTIDは単一の server_uuidになるはず。

となれば :-で切り出せば最後の1つは出せる。

mysql> SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(GTID_SUBTRACT((SELECT gtid_executed FROM sakura.hb WHERE recorded = '2025-07-04 03:34:00'), (SELECT gtid_executed FROM sakura.hb WHERE recorded < '2025-07-04 03:33:00' ORDER BY recorded DESC LIMIT 1)), ':', -1), '-', -1) AS last_gtid_seq;
+---------------+
| last_gtid_seq |
+---------------+
| 1832          |
+---------------+
1 row in set (0.08 sec)

mysql> SELECT CONCAT(@@server_uuid, ':', SUBSTRING_INDEX(SUBSTRING_INDEX(GTID_SUBTRACT((SELECT gtid_executed FROM sakura.hb WHERE recorded = '2025-07-04 03:34:00'), (SELECT gtid_executed FROM sakura.hb
WHERE recorded < '2025-07-04 03:33:00' ORDER BY recorded DESC LIMIT 1)), ':', -1), '-', -1)) AS last_gtid;
+-------------------------------------------+
| last_gtid                                 |
+-------------------------------------------+
| 657f3412-588a-11f0-9230-0242ac110003:1832 |
+-------------------------------------------+
1 row in set (0.08 sec)

これで dump.loadBinlogsに渡す引数が完成した気がする。

もう一つコンテナを起動して、

$ mysql -h172.17.0.5 -uroot -e "SET GLOBAL local_infile=1"
$ mysqlsh root@172.17.0.5 --js -- util loadDump /path/to/another_full_backup/ { --loadUsers=true --ignoreExistingObjects=true --updateGtidSet=replace --skipBinlog=true }

$ mysqlsh root@172.17.0.5 --js -- util loadBinlogs /path/to/another_binlog { --stopAfter=$(mysql -h172.17.0.3 -uroot -sse "SELECT CONCAT(@@server_uuid, ':', SUBSTRING_INDEX(SUBSTRING_INDEX(GTID_SUBTRACT((SELECT gtid_executed FROM sakura.hb WHERE recorded = '2025-07-04 03:34:00'), (SELECT gtid_executed FROM sakura.hb WHERE recorded < '2025-07-04 03:33:00' ORDER BY recorded DESC LIMIT 1)), ':', -1), '-', -1)) AS last_gtid;") }
Please provide the password for 'root@172.17.0.5':
Save password for 'root@172.17.0.5'? [Y]es/[N]o/Ne[v]er (default No):
Loading 2 binlogs, 112.52 KB of data
Opening dump '/path/to/another_binlog'
  Loading dump '2025-07-04-03-31-48' created at 2025-07-04 03:31:48 UTC
    Loading binary log file 'binlog.000003', GTID set: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1687-1701 (5.88 KB)
      Found starting GTID: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1687
  Loading dump '2025-07-04-03-36-11' created at 2025-07-04 03:36:11 UTC
    Loading binary log file 'binlog.000003', GTID set: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1702-1964 (106.64 KB)
      Stopped after GTID: 8e7dfcc0-57b7-11f0-a722-0242ac110003:1832
56% (63.23 KB / 112.52 KB), 0.00 B/s, 0.00 B/s compressed, 0.00 stmts/s, 2 / 2 binlogs done
Total duration: 00:00:00s
Binlogs loaded: 2
Uncompressed data size: 112.52 KB
Compressed data size: 16.59 KB
Statements executed: 1330
Average uncompressed throughput: 63.23 KB/s
Average compressed throughput: 16.59 KB/s
Average statement throughput: 1.33 KB/s

$ mysql -h172.17.0.5 -uroot -e "SELECT * FROM sakura.hb ORDER BY seq DESC LIMIT 1"
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| seq   | hostname     | recorded            | gtid_executed                                                                             |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+
| 78014 | 756eebf85ccb | 2025-07-04 03:33:59 | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1831 |
+-------+--------------+---------------------+-------------------------------------------------------------------------------------------+

$ mysql -h172.17.0.5 -uroot -e "SHOW BINARY LOG STATUS"
+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                         |
+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+
| binlog.000002 |    59702 |              |                  | 3b5d02fb-56e7-11f0-8f01-0242ac110002:1-87887,
8e7dfcc0-57b7-11f0-a722-0242ac110003:1-1832 |
+---------------+----------+--------------+------------------+-------------------------------------------------------------------------------------------+

OKいけた。


大吉祥寺.pm 2025はとても最高でした

$
0
0

大吉祥寺.pm 2025に参加してきました。まずは最高でした。主催の @magnoliakさん、スタッフのみなさま、登壇者のみなさま、参加されたみなさま、懇親会のお店のみなさまみんなどうもありがとうございました!

新型コロナウイルスが世を騒がせた2020年から、外に出てしゃべる機会が激減し、たまにオンラインでやったり、年に1回くらいは外で何かしゃべったり…。そんな生活が4年くらい続いていました。
今年に入ってから以前みたいに活動したいなと強く思っていて、 HeatwaveJP , db tech showcase 2025 Tokyoでしゃべらせてもらって「今年はちょくちょくしゃべってるな、ペース戻ってきたかな」とか思ってたんですが。
どちらもお招きいただいたもので、オフラインでプロポーザルを書いて参加したイベントはたぶん5年前の PHPerKaigi 2020以来。
もともとプロポーザルを書くのは全然上手くないのですが、 転職から1か月半時点で溜まりに溜まった「MariaDBに違和感を感じるなんて、俺はそんなにMySQLが好きだったんだな」感を伝えたくて ( 「2025年の今、みんなに聞いてほしいこと」今、誰かに言っておきたいこと「あの時、こんなこと言ってたなぁ、2025年っぽいー」 ) 応募しました。

きちぴーへの参加は大のつかない 吉祥寺.pm11 以来8年ぶり(!!) 2回目でした。もう8年も経っていたの!?

採択いただいて喜ぶ俺の図。

久々のプロポーザル、久々の吉祥寺(俺は高校が吉祥寺駅に近かったので、昔はよく遊びに来ていたものなんです)、そして久々の @magnoliakさんと三拍子揃った嬉しい登壇でした。
magnoliakさんとは個人的に心の同期感があって、 2015年のYAPC::Asia 2015のLTでご一緒した仲なので、またこうやって同じ会場で話す機会がいただけたのはすごく嬉しく思っています。



(まだ当時の動画が見られます。維持してくれていてとてもありがたい…。)

そんな私のセッションはこんな感じでした。

すばらしいまとめ(ツイートが多すぎて当日自分では到底終えなかった。朝イチだったのでその後もガンガン流れていく)を作っていただけたので、なんとなく雰囲気を感じていただけると嬉しいです。
物語のクライマックスは冒頭3分~5分くらいでスライドがちゃんと映るようになったところでした。あの拍手が無ければ俺はきっとフリーズしていたでしょう。。

大吉祥寺.pm 2025 のまとめ #kichijojipm - posfie

ちょうど本業でMariaDBとMySQLのことを思って誰かに話したい気分だったことと、調べてみたら以前(俺がMariaDBを真面目に比較していたのは10.0くらいの時までだった)と割と状況が違っていたので、そのあたり昔話や経緯も含めてざっとまとめられたかなと思っています。

なお、ランチマッチングで若者とカレーを食べに行きました(俺が高校生だった25年前とかに行ったことがあるお店な気がする。建物にはあんまりピンと来ませんでしたが確かあんなお店の名前だったような…)

セッショントークも面白かったし、ライトニングトークは本当に笑わせてもらいました。カンパもしました。いい一日だった!

みなさん本当にありがとうございました!

自分のためのMySQLのSandboxが欲しかったのでyt-sandboxを作った

$
0
0

この記事は MySQL - Qiita Advent Calendar 2025の4日目の記事です。
昨日は @meijikさんの 各ご家庭(Firebird/MySQL/PostgreSQL)のUUID事情(主に4と7)でした。


MySQLで自分用のサンドボックス環境…といえば長年 MySQL::Sandboxというのがあって、mysqldのバイナリさえあればある程度好きにバカバカとバージョン違いのプロセスを起動したり、 make_replication_sandboxだけで好きにレプリケーションクラスタ一式を起動したりということができました。これはもうだいぶ前にメンテナンスが終わっていて、確か8.0くらいは軽くゴニョって使えたけど8.4はもう無理という代物。

その後継として同じ人が dbdeployerというのを出していたけれど、8.4で MySQL::SandBoxが完全に使えなくなったら引っ越そうかなと思ったら引っ越す前にメンテナンスが終わった。

まだ生きてるサンドボックス作成ツールとして MySQL Shell の dba.deploySandboxInstanceも使ったりはするけどこれは単発でmysqldを(自分でポートを指定して)起動するだけで、レプリケーションを勝手に組んだりしてくれないのでちょっとこの用途としてはちょっと合わない。
当時はmysqldは勝手に /usr/sbin/mysqld とかから探されてしまったけど、最近のリリースで mysqlPathというmysqldバイナリのパスを指定できるようになったのでまたそのうち使い始めるかもしれない。

というわけで、自分用に作りました yt-sandbox
ytkitのスクリプトなので

$ git clone https://github.com/yoku0825/ytkit.git
$ cd ytkit
$ cpanm --installdeps .
$ bin/yt-sandbox

で使えるようになりますが、飽くまで自分で使う用途がメインなので中身でrootless dockerに依存して、dockerのデフォルトネットワークがbridgeでないと動かないとかそんな諸々があります。

シンプルな使い方。

$ bin/yt-sandbox --mysqld=5.5
[7729] NOTE: Generate Sandbox directry into /home/yoku0825/yt-sandbox/alpha
[7729] NOTE: Node1 Container Ipaddress: 172.17.0.2
Sandbox deployed into /home/yoku0825/yt-sandbox/alpha

--mysqldでバージョンを渡すとそのタグのコンテナイメージで docker runします。普通に docker runで指定するだけなので、 8.4.5時代に 8.4で起動していると 8.4のタグが 8.4.5に向いたままとか起こるような気がします。なお、 latestは対応してません。
古めのバージョンは Docker Hub から、新しめのバージョンはOracleのコンテナハブからダウンロードしてきます。

ターミナルにも出力しますが、サンドボックスディレクトリを作ってその中に色々ぶち込みます。

$ cd /home/yoku0825/yt-sandbox/alpha
$ ll
total 28
-rw-r--r-- 1 yoku0825 yoku0825   98 Dec  4 10:27 destroy_all
-rw-r--r-- 1 yoku0825 yoku0825   19 Dec  4 10:27 hosts
lrwxrwxrwx 1 yoku0825 yoku0825   41 Dec  4 10:27 n1 -> /home/yoku0825/yt-sandbox/alpha/node1/use
drwxr-xr-x 3 yoku0825 yoku0825 4096 Dec  4 10:27 node1
-rwxr-xr-x 1 yoku0825 yoku0825   98 Dec  4 10:27 restart_all
-rwxr-xr-x 1 yoku0825 yoku0825   96 Dec  4 10:27 start_all
-rwxr-xr-x 1 yoku0825 yoku0825   95 Dec  4 10:27 stop_all
-rwxr-xr-x 1 yoku0825 yoku0825   94 Dec  4 10:27 use_all

MySQL::Sandboxはこのディレクトリにちゃんと useとかがあったんですが、yt-sandboxはサーバー数が1かそれ以上かを区別しないので useとかは個別の node1ディレクトリの中に入ってます。

単発のインスタンスで良ければdockerコマンド単体で上げちゃうのでこれはあんまり使っていないかも。

$ yt-sandbox -t replication --servers 2
[8333] NOTE: Generate Sandbox directry into /home/yoku0825/yt-sandbox/charlie
Unable to find image 'container-registry.oracle.com/mysql/community-server:8.4' locally
8.4: Pulling from mysql/community-server
c1796c1f2872: Pull complete
73dc7709f5dd: Pull complete
3ba20a201270: Pull complete
bcf1148c6894: Pull complete
ea899417b705: Pull complete
017f83ce814a: Pull complete
d120c25676a4: Pull complete
Digest: sha256:5e274b0e244563921fe9b520f4a2dec301ffc4fc81164a362c6cb3dac60255a9
Status: Downloaded newer image for container-registry.oracle.com/mysql/community-server:8.4
[8333] NOTE: Node1 Container Ipaddress: 172.17.0.2
[8333] NOTE: Node2 Container Ipaddress: 172.17.0.3
Sandbox deployed into /home/yoku0825/yt-sandbox/charlie

-t replicationでレプリケーションクラスタを作れます。 -t gr (Group Replication) もあります。
--serversでクラスタトータルの台数を指定できます。↑の場合はソースレプリカ足して2なので1台ずつ作成されます。

$ cd /home/yoku0825/yt-sandbox/charlie
$ ll
total 36
-rwxr-xr-x 1 yoku0825 yoku0825  259 Dec  4 10:39 check_replication
-rw-r--r-- 1 yoku0825 yoku0825  100 Dec  4 10:39 destroy_all
-rw-r--r-- 1 yoku0825 yoku0825   42 Dec  4 10:39 hosts
lrwxrwxrwx 1 yoku0825 yoku0825   43 Dec  4 10:39 m -> /home/yoku0825/yt-sandbox/charlie/node1/use
drwxr-xr-x 3 yoku0825 yoku0825 4096 Dec  4 10:39 node1
drwxr-xr-x 3 yoku0825 yoku0825 4096 Dec  4 10:39 node2
-rwxr-xr-x 1 yoku0825 yoku0825  100 Dec  4 10:39 restart_all
lrwxrwxrwx 1 yoku0825 yoku0825   43 Dec  4 10:39 s1 -> /home/yoku0825/yt-sandbox/charlie/node2/use
-rwxr-xr-x 1 yoku0825 yoku0825   98 Dec  4 10:39 start_all
-rwxr-xr-x 1 yoku0825 yoku0825   97 Dec  4 10:39 stop_all
-rwxr-xr-x 1 yoku0825 yoku0825   96 Dec  4 10:39 use_all

MySQL::Sandbox っぽく、 ./mでソースにつながったり、 ./s1でレプリカに繋がったりします。最初から gtid_mode=ONです。

レプリケーションはIPアドレスに依存していて、dockerコンテナは上げたり下げたりするとIPアドレスが変わる(俺はそういう使い方しかしていない)ので、一度でもstopするとレプリケーションは壊れることがあります。

基本は、

  • 作って
  • 試して
  • すぐ捨てる
    がコンセプトです。

root@%がパスワードなしで公開されているので注意してください。サンドボックスです。

明日は @taka_yuki_04さんです!

今年もConoHaのVPSでMySQLをビルドする 2025年

$
0
0

この記事は ConoHa - Qiita Advent Calendar 2025の7番目の窓です。

昨日は 8mitsuさんの ConoHaをMCPで操作してみたでした。

この記事は MySQL - Qiita Advent Calendar 2025の7番目の窓でもあります。

こちらは昨日は @taka_yuki_04さんの MySQL キーリングプラグインからコンポーネントへの移行でした。


今年はビルドのために 6コア6GBのOracle Linux 10を起動しました。去年までは1コア2GBで頑張っていましたがそれで最近のMySQLをビルドしていると一晩かかるのでもうつらい。


OSはOracle Linuxの10にしてみた。


$ cat /etc/os-release

NAME="Oracle Linux Server"

VERSION="10.1"

ID="ol"

ID_LIKE="fedora"

VARIANT="Server"

VARIANT_ID="server"

VERSION_ID="10.1"

PLATFORM_ID="platform:el10"

PRETTY_NAME="Oracle Linux Server 10.1"

ANSI_COLOR="0;31"

CPE_NAME="cpe:/o:oracle:linux:10:1:server"

HOME_URL="https://linux.oracle.com/"

BUG_REPORT_URL="https://github.com/oracle/oracle-linux"

ORACLE_BUGZILLA_PRODUCT="Oracle Linux 10"

ORACLE_BUGZILLA_PRODUCT_VERSION=10.1

ORACLE_SUPPORT_PRODUCT="Oracle Linux"

ORACLE_SUPPORT_PRODUCT_VERSION=10.1

まずは来年、2026年4月にEOLになるMySQL 8.0シリーズの最新版、8.0.44。


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

$ tar xf mysql-boost-8.0.44.tar.gz
$ cmake .
-bash: cmake: command not found

$ sudo dnf install -y cmake

$ cmake .
-- Running cmake version 3.30.5
-- Could NOT find Git (missing: GIT_EXECUTABLE)
Hint: The project() command has not yet been called.  It sets up system-specific search paths.
-- This is .el10. as found from 'rpm -qf /'
-- CMAKE_MODULE_PATH is /home/yoku0825/mysql-8.0.44/cmake
-- MySQL 8.0.44
-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
CMake Error at CMakeLists.txt:585 (PROJECT):
  No CMAKE_C_COMPILER could be found.

  Tell CMake where to find the compiler by setting either the environment
  variable "CC" or the CMake cache entry CMAKE_C_COMPILER to the full path to
  the compiler, or to the compiler name if it is in the PATH.

CMake Error at CMakeLists.txt:585 (PROJECT):
  No CMAKE_CXX_COMPILER could be found.

  Tell CMake where to find the compiler by setting either the environment
  variable "CXX" or the CMake cache entry CMAKE_CXX_COMPILER to the full path
  to the compiler, or to the compiler name if it is in the PATH.

-- Configuring incomplete, errors occurred!

コンパイラがないので入れる。去年(Rocky Linux 9.4)の時は「このgcc-toolsetを入れろ」って書いてあったけど10系だからかそんなに親切ではない感じ。

$ sudo dnf install -y gcc gcc-c++

$ cmake .
..
-- Binary directory /home/yoku0825/mysql-8.0.44
CMake Error at CMakeLists.txt:639 (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!

忘れてた。インソースビルドでいいので足す。あとBoost同梱版なのでそれも足す。さらについでにインストール先の指定もくっつける。

$ cmake -DCMAKE_INSTALL_PREFIX=/usr/mysql/8.0.44 -DFORCE_INSOURCE_BUILD=1 -DWITH_BOOST=./boost .
..
-- ZSTD_LEGACY_SUPPORT not defined!
--
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:406 (FATAL_SSL_NOT_FOUND_ERROR)
  CMakeLists.txt:1848 (MYSQL_CHECK_SSL)

-- Configuring incomplete, errors occurred!

openssl-develが必要、はい。

$ sudo dnf install -y openssl-devel

$ cmake -DCMAKE_INSTALL_PREFIX=/usr/mysql/8.0.44 -DFORCE_INSOURCE_BUILD=1 -DWITH_BOOST=./boost .

..
-- Could NOT find Curses (missing: CURSES_LIBRARY CURSES_INCLUDE_PATH)
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:1952 (MYSQL_CHECK_EDITLINE)

-- Configuring incomplete, errors occurred!

ncurses-develも必要。で入れたらCMakeCache.txtを消せと(なんでこのパッケージだけ消さないとダメなのか俺はよくわかっていない…)

$ sudo dnf install -y ncurses-devel
$ rm CMakeCache.txt
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/mysql/8.0.44 -DFORCE_INSOURCE_BUILD=1 -DWITH_BOOST=./boost .
..
-- 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:112 (WARN_MISSING_SYSTEM_TIRPC)
  CMakeLists.txt:2088 (MYSQL_CHECK_RPC)

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

-- Configuring incomplete, errors occurred!

毎年リポジトリがよくわからなくなるtirpc-devel 。Oracle Linux 10では ol10_codeready_builderらしい。

$ sudo dnf install libtirpc-devel

Last metadata expiration check: 0:23:40 ago on Sat 06 Dec 2025 06:44:15 PM JST.
No match for argument: libtirpc-devel
Error: Unable to find a match: libtirpc-devel

$ sudo dnf install -y --enablerepo="*" libtirpc-devel
..
=======================================================================================================================================
 Package                         Architecture            Version                         Repository                               Size
=======================================================================================================================================
Installing:
 libtirpc-devel                  x86_64                  1.3.5-1.el10                    ol10_codeready_builder                  187 k
..

$ cmake -DCMAKE_INSTALL_PREFIX=/usr/mysql/8.0.44 -DFORCE_INSOURCE_BUILD=1 -DWITH_BOOST=./boost .
..

-- Performing Test X_PUTLONG_NOT_USE_CONST - Failed
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)

-- Configuring incomplete, errors occurred!

また毎年リポジトリのはぐれるrpcgen

$ sudo dnf install -y --enablerepo="*" rpcgen

..
=======================================================================================================================================
 Package                     Architecture                Version                             Repository                           Size
=======================================================================================================================================
Installing:
 rpcgen                      x86_64                      1.4-17.el10                         ol10_appstream                       55 k
..

$ cmake -DCMAKE_INSTALL_PREFIX=/usr/mysql/8.0.44 -DFORCE_INSOURCE_BUILD=1 -DWITH_BOOST=./boost .
..

-- CMAKE_SHARED_LINKER_FLAGS
-- Configuring done (3.2s)
-- Generating done (5.2s)
-- Build files have been written to: /home/yoku0825/mysql-8.0.44

$ time make -j$(nproc)
..
real    41m31.176s
user    225m37.415s
sys     15m19.890s

$ sudo make install

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

OK。次に今のメインストリームの8.4.7。Boostは同梱されるようになったから -DWITH_BOOSTはもういらない。

$ wget https://dev.mysql.com/get/Downloads/MySQL-8.4/mysql-8.4.7.tar.gz
$ tar xf mysql-8.4.7.tar.gz
$ cd mysql-8.4.7/
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/mysql/8.4.7 -DFORCE_INSOURCE_BUILD=1 .

$ time make -j$(nproc)
..
/home/yoku0825/mysql-8.4.7/extra/boost/boost_1_84_0/boost/multiprecision/cpp_bin_float.hpp:40:10: fatal error: quadmath.h: No such file or directory
   40 | #include <quadmath.h>
      |          ^~~~~~~~~~~~
compilation terminated.
make[2]: *** [sql/CMakeFiles/sql_gis.dir/build.make:90: sql/CMakeFiles/sql_gis.dir/gis/buffer.cc.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:49710: sql/CMakeFiles/sql_gis.dir/all] Error 2
make: *** [Makefile:166: all] Error 2

real    39m24.234s
user    217m18.669s
sys     13m42.765s

エラった…。 libquadmath-devel が必要そう。

$ sudo dnf install libquadmath-devel
$ time make -j$(nproc)
..
real    19m49.002s
user    106m29.194s
sys     6m19.764s

合計49分くらい。サイズはそんな変わらない。

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

最後にInnovation Releaseの9.5.0

$ wget https://dev.mysql.com/get/Downloads/MySQL-9.5/mysql-9.5.0.tar.gz

$ tar xf mysql-9.5.0.tar.gz
$ cd mysql-9.5.0/
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/mysql/9.5.0 -DFORCE_INSOURCE_BUILD=1 .
..
-- Could NOT find BISON (missing: BISON_EXECUTABLE)
CMake Warning at cmake/bison.cmake:119 (MESSAGE):
  No bison found!!
Call Stack (most recent call first):
  router/src/routing_guidelines/src/CMakeLists.txt:25 (INCLUDE)

CMake Warning at cmake/bison.cmake:120 (MESSAGE):
  If you have bison in a non-standard location, you can do 'cmake
  -DBISON_EXECUTABLE=</path/to/bison-executable>
Call Stack (most recent call first):
  router/src/routing_guidelines/src/CMakeLists.txt:25 (INCLUDE)

CMake Error at /usr/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:233 (message):
  Could NOT find BISON (missing: BISON_EXECUTABLE)
Call Stack (most recent call first):
  /usr/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:603 (_FPHSA_FAILURE_MESSAGE)
  /usr/share/cmake/Modules/FindBISON.cmake:306 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
  router/src/routing_guidelines/src/CMakeLists.txt:27 (FIND_PACKAGE)

-- Configuring incomplete, errors occurred!

Bisonが必須になったみたい(以前は任意でワーニングは出たけどエラーではなかった)

$ sudo dnf install -y bison
$ cmake -DCMAKE_INSTALL_PREFIX=/usr/mysql/9.5.0 -DFORCE_INSOURCE_BUILD=1 .

$ time make -j$(nproc)
..
real    59m43.890s
user    328m0.973s
sys     19m57.807s

6コア使って1時間じゃ、1コアだとホントにメモリも足りないしダメなんではなかろうか。。

$ du -sh /usr/mysql/9.5.0/
1.6G    /usr/mysql/9.5.0/
明日は ConoHa - Qiita Advent Calendar 2025 が @che_azure さん、 MySQL - Qiita Advent Calendar 2025 が @HrsUedさんです! ​
a

最近のmysqlコマンドラインクライアントの変更を見てみる

$
0
0

この記事は MySQL - Qiita Advent Calendar 2025の15日目の記事です。

昨日は @meijikさんの 君はMySQL RP を知っているか?!でした。


ここ最近(?) 小さな mysqlコマンドラインクライアントへの変更が入っているのでそれを紹介します。

MySQLサーバー ( mysqld ) の機能ではないので、接続先のバージョンがいくつであっても変わりません。クライアントのバージョン依存です。

コメントが勝手に消えなくなった

実は mysqlコマンドラインクライアントには「SQLの中のコメントを消してからMySQLサーバに送信する」機能がありました。バージョン指定のためのコメントやオプティマイザヒントは消さないようになっています。純粋なコメントです。

以下は各バージョンの mysqlから MySQL 8.4.7のサーバに対して SELECT 'Hello' /* ,World/;'を送信しようとしているところです( <<<で標準入力に入るので、 echo .. | mysql ..と同じ実行の仕方です)


$ /usr/mysql/5.7.44/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "SELECT 'Hello' /* ,World */;"

Hello

Hello

$ /usr/mysql/8.0.44/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "SELECT 'Hello' /* ,World */;"

Hello

Hello

$ /usr/mysql/8.4.7/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "SELECT 'Hello' /* ,World */;"

Hello

Hello

$ /usr/mysql/9.5.0/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "SELECT 'Hello' /* ,World */;"

Hello

Hello

ジェネラルログを確認するとこんな感じになります。


2025-12-15T09:04:30.826100-00:00           17 Connect   root@localhost on  using Socket

2025-12-15T09:04:30.829805-00:00           17 Query     select @@version_comment limit 1

2025-12-15T09:04:30.834689-00:00           17 Query     SELECT 'Hello'

2025-12-15T09:04:30.834788-00:00           17 Quit

2025-12-15T09:04:33.468778-00:00           18 Connect   root@localhost on  using Socket

2025-12-15T09:04:33.472739-00:00           18 Query     select @@version_comment limit 1

2025-12-15T09:04:33.478144-00:00           18 Query     SELECT 'Hello'

2025-12-15T09:04:33.478406-00:00           18 Quit

2025-12-15T09:04:36.309772-00:00           19 Connect   root@localhost on  using Socket

2025-12-15T09:04:36.312713-00:00           19 Query     select @@version_comment limit 1

2025-12-15T09:04:36.319830-00:00           19 Query     SELECT 'Hello' /* ,World */

2025-12-15T09:04:36.321674-00:00           19 Quit

2025-12-15T09:04:38.924431-00:00           20 Connect   root@localhost on  using Socket

2025-12-15T09:04:38.928317-00:00           20 Query     select @@version_comment limit 1

2025-12-15T09:04:38.935416-00:00           20 Query     SELECT 'Hello' /* ,World */

2025-12-15T09:04:38.935519-00:00           20 Quit

上から5.7, 8.0, 8.4, 9.5なので8.4の時点で勝手にコメントをストリップしなくなりました。

昔からずっと —commentsというオプションで制御できたんですが、8.0とそれ以前はこれのデフォルトがOFF, 8.4とそれ以降はONに変わったという感じです。

/* .. */で実験していますが、 --#も消えます。お試しあれ(?)
以前の動作に戻すなら --skip-commentsです。

デフォルトでは \Gで縦表示できなくなった

SHOW ENGINE INNODB STATUS\GSHOW REPLICA STATUS\Gはよく縦表示に用いるんじゃないかと思いますが、この \Gmysqlコマンドラインクライアントの独自記法で、その他のクライアントやアプリケーションから使うときに \Gとかつけるとシンタックスエラーになります。

-eや標準入力からSQLを受け取った時の mysqlコマンドラインクライアントは今までこの \Gをデフォルト(あるいは設定不能)で常に解釈していたのが、9.x のどこかで解釈しない方がデフォルトになりました。

$ /usr/mysql/5.7.44/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "SHOW ENGINE INNODB STATUS\G" | head -2
*************************** 1. row ***************************
  Type: InnoDB

$ /usr/mysql/8.0.44/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "SHOW ENGINE INNODB STATUS\G" | head -2
*************************** 1. row ***************************
  Type: InnoDB

$ /usr/mysql/8.4.7/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "SHOW ENGINE INNODB STATUS\G" | head -2
*************************** 1. row ***************************
  Type: InnoDB

$ /usr/mysql/9.5.0/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "SHOW ENGINE INNODB STATUS\G" | head -2
ERROR at line 1: Unknown command '\G'.

↑9.5のクライアントだけエラーになっています。

なおこれは非対話モードにだけ効く変更なので、対話モードの動作には変わりありません。9.5のクライアントで同じオプションでも、対話モードなら↓のように表示ができます。

$ /usr/mysql/9.5.0/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 31
Server version: 8.4.7 Source distribution

Copyright (c) 2000, 2025, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW ENGINE INNODB STATUS\G
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
2025-12-15 09:21:22 139964134168320 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 12 seconds

..

ちなみに俺は昔から -Eを使うのに慣れているのであまりこの変更の影響を受けません。

-Eは引数を取らない、 psqlで言うところの -xみたいに振る舞うオプションです。 -eとセットで使う時に -eEと書くとエラーになります( -eの引数が 'E'という扱いになってしまう) -Eeで並べる(もしくは -E -eと離したり別のオプションを挟んでみたりする)必要があります。

$ /usr/mysql/5.7.44/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock -E <<< "SHOW ENGINE INNODB STATUS" | head -2
*************************** 1. row ***************************
  Type: InnoDB

$ /usr/mysql/8.0.44/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock -E <<< "SHOW ENGINE INNODB STATUS" | head -2
*************************** 1. row ***************************
  Type: InnoDB

$ /usr/mysql/8.4.7/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock -E <<< "SHOW ENGINE INNODB STATUS" | head -2
*************************** 1. row ***************************
  Type: InnoDB

$ /usr/mysql/9.5.0/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock -E <<< "SHOW ENGINE INNODB STATUS" | head -2
*************************** 1. row ***************************
  Type: InnoDB

-Eはずっと昔からあるのでこれを使っていると9.xでの変更に気が付かない可能性があります。なお、以前と同じ動作にするには —commandsです。

この動作変更は非互換としてバグ報告されており、デフォルトをもとに戻してほしい場合はこのバグレポートで応援することができます。

MySQL Bugs: #118819: Vertical (\G) doesn't work in MySQL 9.4.0


$ /usr/mysql/9.5.0/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock --commands <<< "SHOW ENGINE INNODB STATUS\G" | head -2
*************************** 1. row ***************************
  Type: InnoDB

8.4の地味なバグの修正

少なくともMySQL 8.4.7の mysqlコマンドラインクライアントにある地味なバグが直っています。非対話モードで useにセミコロンを1個余計につけるとエラーになります。

$ /usr/mysql/5.7.44/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "use d1;;"

$ /usr/mysql/8.0.44/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "use d1;;"

$ /usr/mysql/8.4.7/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "use d1;;"
ERROR at line 1: USE must be followed by a database name

$ /usr/mysql/9.5.0/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock <<< "use d1;;"

対話モードでも、他のバージョンとはエラーが違います。
8.4以外のバージョンでは↓のように

$ /usr/mysql/9.5.0/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock

mysql> use d1;;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
ERROR:
No query specified

use d1;が解釈された後に本文なしの ;が来たと解釈して、Database changedの後に No query specifiedのエラーになります(エラーエラー言ってますが、これはサーバーサイドエラーではなく mysqlコマンドラインクライアント内で独自にエラーです)

8.4のコマンドラインクライアントでは

$ /usr/mysql/8.4.7/bin/mysql --no-defaults -uroot -S /usr/mysql/8.4.7/data/mysql.sock

mysql> use d1;;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
ERROR:
USE must be followed by a database name

エラーが USE must be followed by a database nameに変わってました。 use d1;を解釈した後に use ;と解釈しなおしたような気配がします。

これはこの記事を書くために git logを眺めていて気が付いただけです。バグなのでそのうち直ると思いますし直らなくても特に困る人はいなさそう。。

History for client/mysql.cc - mysql/mysql-server · GitHub

明日は @wsigma21さんです!

MySQL RouterのRead-Write Splitting…の前に、前提条件になっているConnection Sharingについて見てみたらMySQL Proxyのことを思い出した

$
0
0

この記事は MySQL - Qiita Advent Calendar 2025の21日目の記事です。

昨日は @meijikさんの MedianやPercentile_%が欲しい連合会(Firebird/MySQL)でした。


ふと MySQL RouterのRead-Write Splittingの設定を見ていたら、前提条件として connection_sharing: must be set to 1.と書いてあって、俺が使っていた8.0の頃のRouterと比べて増えたなあと思って調べてみた。

MySQL :: MySQL Router 8.4 :: 3.4 Connection Sharing and Reuse

MySQL Router enables server connections to be pooled and shared

ってことなので、MySQLとMySQL Routerの間のコネクションを、MySQL Routerとアプリケーションの間で使い回させられるような感じになるはず。

折角なので(?) 最新のMySQL Router(Innovation Release)にしてみる(8.4でもできたんだけどなんか勘違いして最新版を入れてしまったのでそれでやる)

$ sudo dnf install mysql-router --disablerepo="*" --enablerepo="mysql-tools-innovation*"
..
=======================================================================================================================================
 Package                             Architecture        Version                   Repository                                     Size
=======================================================================================================================================
Upgrading:
 mysql-router-community              x86_64              9.5.0-1.el8               mysql-tools-innovation-community               61 M
..

mysqlrouter.confを昔取った杵柄でテキトーにいじってみる。

$ sudo vim /etc/mysqlrouter/mysqlrouter.conf
..
[connection_pool]
idle_timeout= 3600
max_idle_server_connections= 10

[routing:test_connection_sharing]
bind_address= 127.0.0.1
bind_port= 49825
destinations= 127.0.0.1:64080
routing_strategy= first-available
connection_sharing= 1
client_ssl_mode = disabled

$ sudo systemctl start mysqlrouter

$ sudo less /var/log/mysqlrouter/mysqlrouter.log
2025-12-21 13:25:24 main SYSTEM [7f938e74b580] Starting 'MySQL Router', version: 9.5.0 (MySQL Community - GPL)
2025-12-21 13:25:24 io INFO [7f938e74b580] starting 8 io-threads, using backend 'linux_epoll'
2025-12-21 13:25:24 keepalive INFO [7f93817fa700] keepalive started with interval 60
2025-12-21 13:25:24 keepalive INFO [7f93817fa700] keepalive
2025-12-21 13:25:24 routing INFO [7f9380ff9700] [routing:test_connection_sharing] started: routing strategy = first-available
2025-12-21 13:25:24 routing INFO [7f9380ff9700] Start accepting connections for routing routing:test_connection_sharing listening on '127.0.0.1:49825'

max_idle_server_connectionsidle_timeout[connection_pool]セクションなので [routing:*]セクションに書いても読んでくれない。

ともあれこれで

$ mysql -h127.0.0.1 -P49825 -uyoku0825 -sse "SELECT CONNECTION_ID()"
35467

$ mysql -h127.0.0.1 -P49825 -uyoku0825 -sse "SELECT CONNECTION_ID()"
35467

$ mysql -h127.0.0.1 -P49825 -uyoku0825 -sse "SELECT CONNECTION_ID()"
35467> 

$ mysql -h127.0.0.1 -P49825 -uyoku0825 -sse "SELECT CONNECTION_ID()"
35467

うん、クライアントを切断しても CONNECTION_ID()の結果が変わらないのでプーリング(シェアリング)できてるっぽい。

で、ここからが本題。

3.4 Connection Sharing and Reuseを読むと、

Unsupported SQL Features
The following statements and functions are not supported when connection sharing is active, except inside a transaction.
..

  • LAST_INSERT_ID()

と、コネクションシェアリングでは使えないらしい関数が示されている。

$ mysql -h127.0.0.1 -P49825 -uyoku0825

mysql> SELECT LAST_INSERT_ID();
ERROR 3566 (HY000): Access to native function is rejected when connection sharing is enabled

確かにエラーになる。

$ ./runtime_output_directory/perror 3566
MySQL error code MY-003566 (ER_NO_ACCESS_TO_NATIVE_FCT): Access to native function '%.64s' is rejected.

MySQLサーバー側のperrorでもちゃんと返ってきたけれど、なんかよく見るとエラーメッセージがしっくり来ていない ( %.64sis rejectedshwn connection sharing is enabledが綺麗に対応していない )

あと、 performance_schema.events_errors_summary_global_by_errorに記録されてないのでこれはMySQLサーバから来てるんじゃなくてMySQL Routerがエラーパケット作ってるっぽい。

mysql80 35513> SELECT * FROM events_errors_summary_global_by_error WHERE error_number = 3566;
+--------------+----------------------------+-----------+------------------+-------------------+------------+-----------+
| ERROR_NUMBER | ERROR_NAME                 | SQL_STATE | SUM_ERROR_RAISED | SUM_ERROR_HANDLED | FIRST_SEEN | LAST_SEEN |
+--------------+----------------------------+-----------+------------------+-------------------+------------+-----------+
|         3566 | ER_NO_ACCESS_TO_NATIVE_FCT | HY000     |                0 |                 0 | NULL       | NULL      |
+--------------+----------------------------+-----------+------------------+-------------------+------------+-----------+
1 row in set (0.00 sec)

さて。

こんなこと ( = MySQL Routerが中を通るSQLに応じて勝手にエラーパケットを返す ) ができるということは、つまりMySQL Routerは 流れてくるSQLをパースしているわけで、そうすると Limitationsに書かれている Connection sharing is not supported in PASSTHROUGH mode or if server-ssl-mode=AS_CLIENT and client-ssl-mode=PREFERRED.も、mysqlrouterが終端しない形でSSL/TLSを使ってしまうと流れてくるSQLが読めなくなるからなわけであって、てことはコネクションシェアリングを前提にしているRead-Write SplittingというのはMySQL RouterがSQLをパースしてソースに投げるかレプリカに投げるのかを打ち分けているということで…。

MySQL Proxyの rw-splitting.luaとか思い出して見てみたら、弾いてる関数とかが似ていて、繰り返す歴史を感じたという感じでした。

明日は @tmtmsさんです!

MySQL 8.0のEOLに際して、ぼくと8.0のおもいで

$
0
0

この記事は MySQL - Qiita Advent Calendar 2025の25日目の記事です。メリークリスマス!

昨日は日本で一番MySQL Studioに詳しいという噂の mikomaさんによる MySQL AIとMySQL Studioをざっくりとでした。


さて掲題のとおりMySQL 8.0が来年 2026年の4月にEOL (End Of Lifetime) を迎えるわけですが、MySQL 8.0の思い出でもちょっと。

一言で言うと「あれだけ我々を ~苦しめて~ 盛り上げてきた8.0が終わるという感じ」

MySQL 8.0の話はずっとしていたような気がします。が、思えばGAが2018年で、俺がMySQLを本格的に始めたのは2012年なので、MySQL人生の半分はMySQL 8.0でした。そりゃずっと話してる気もするわって感じなのです。



まだ8.0がGAになる前…楽しそうですね俺。
5.7は ~MySQL Fabricとともに~ 結構新機能を使い倒した感があったので、更に機能が増える(であろう) 8.0にとても期待していたのがわかります。



実際に8.0がロールアウトされて、本番導入をほぼほぼ意識していた(というかこの時はもう現用環境とレプリケーションを組んでいたはず)頃。まだ ~悪夢ではない~ 夢を見ていそうです。



ほぼ同時期のスライド。「パッチバージョンで非互換wwww」とか言っていたものの、自分の本番環境ではまだカットオーバーされていないからか笑い話にしているフシがありますね。


致命的なバグを含まない最新版MySQLを探すには? 『MySQL徹底入門』共著者が語る、バージョン選びのポイント | ログミーBusiness


このへんでコロナ禍を挟んで活動が減ってたり転職したりしてますが、2022年には8.0を楽しむどころかこうなっていた模様。



その間にMySQL 4.0(バージョン50%削減)と付き合ったりもしていました。



8.4のリリースで「安心して使える8.0だと思えば」と言っているあたり、8.0時代が本当につらかったんだなあと思い起こさせます。



今はなんと8.xを飛び越えて10.xの世界線にいますが、またいずれ9.7の世界線で会えることを楽しみにしています。

さあ、それはさておきMySQL 8.0のEOLは来年 2026年の4月ですからね! あと1営業日しかありませんが今から8.4に行く準備をしておきましょう。良いお年を!

DBD::mysqlで mysql_ssl => 1すると接続が SSL connection error: self signed certificate で転ける理由

$
0
0

TL;DR

  • 単に mysql_ssl=1で接続しようとすると転ける

  • mysql_ssl=1;mysql_ssl_verify_server_cert=0なら良いかと思うと SSL connection error: Enforcing SSL encryption is not supported without mysql_ssl_verify_server_certで転ける

  • DBD::mysql が libmysqlclient.soではなく libmariadb.soとリンクされているとこうなる


$ ldd $(rpm -ql perl-DBD-MySQL | grep mysql.so)

        linux-vdso.so.1 (0x00007fff0e77b000)

        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f23f7a07000)

        libmariadb.so.3 => /lib64/libmariadb.so.3 (0x00007f23f77b2000)      <---- いた

        libperl.so.5.26 => /lib64/libperl.so.5.26 (0x00007f23f73a3000)

        libc.so.6 => /lib64/libc.so.6 (0x00007f23f6fcc000)

        /lib64/ld-linux-x86-64.so.2 (0x00007f23f7e47000)

        libz.so.1 => /lib64/libz.so.1 (0x00007f23f6db4000)

        libdl.so.2 => /lib64/libdl.so.2 (0x00007f23f6bb0000)

        libm.so.6 => /lib64/libm.so.6 (0x00007f23f682e000)

        libssl.so.1.1 => /lib64/libssl.so.1.1 (0x00007f23f6599000)

        libcrypto.so.1.1 => /lib64/libcrypto.so.1.1 (0x00007f23f60ae000)

        libresolv.so.2 => /lib64/libresolv.so.2 (0x00007f23f5e96000)

        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f23f5c6d000)

        libutil.so.1 => /lib64/libutil.so.1 (0x00007f23f5a69000)

単に mysql --ssl-mode=requiredと同じことをしたいだけなのに

$ mysql -h 127.0.0.1 -P 64080 -u yoku0825 --ssl-mode=required -sse "SHOW SESSION STATUS LIKE 'Ssl_cipher'"
Ssl_cipher      TLS_AES_128_GCM_SHA256

$ perl -MDBI -MData::Dumper -E 'my $conn= DBI->connect("dbi:mysql:;host=127.0.0.1;port=64080;mysql_ssl=1", "yoku0825")'
DBI connect(';host=127.0.0.1;port=64080;mysql_ssl=1','yoku0825',...) failed: SSL connection error: self signed certificate in certificate chain at -e line 1.

怒られる。
証明書が自己署名なのはそう(単に mysqld --initializeの時の auto_generate_certs=ONの設定で作られるやつだから)だけど、エラーになってほしくはない。でも mysql_ssl=0にはしたくない(経路の暗号化はしてほしい)

というか、 mysqlコマンドラインクライアントは文句を言わないので DBD::mysqlの時だけ何かがおかしい。ということは。

$ rpm -ql perl-DBD-MySQL | grep mysql.so
/usr/lib64/perl5/vendor_perl/auto/DBD/mysql/mysql.so

$ ldd /usr/lib64/perl5/vendor_perl/auto/DBD/mysql/mysql.so
        linux-vdso.so.1 (0x00007ffc71bcb000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fe5bb2d7000)
        libmariadb.so.3 => /lib64/libmariadb.so.3 (0x00007fe5bb082000)
        libz.so.1 => /lib64/libz.so.1 (0x00007fe5bae6a000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007fe5bac66000)
        libm.so.6 => /lib64/libm.so.6 (0x00007fe5ba8e4000)
        libssl.so.1.1 => /lib64/libssl.so.1.1 (0x00007fe5ba64f000)
        libcrypto.so.1.1 => /lib64/libcrypto.so.1.1 (0x00007fe5ba164000)
        libperl.so.5.26 => /lib64/libperl.so.5.26 (0x00007fe5b9d55000)
        libc.so.6 => /lib64/libc.so.6 (0x00007fe5b997f000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fe5bb717000)
        libresolv.so.2 => /lib64/libresolv.so.2 (0x00007fe5b9767000)
        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007fe5b953e000)
        libutil.so.1 => /lib64/libutil.so.1 (0x00007fe5b933a000)

やっぱりlibmariadbにリンクされてる。
libmysqlclientで試してみるために cpanm --lookでビルドしてみる。

$ cpanm --look DBD::mysql
$ perl Makefile.PL

PLEASE NOTE:

For 'make test' to run properly, you must ensure that the
database user 'yoku0825' can connect to your MySQL server
and has the proper privileges that these tests require such
as 'drop table', 'create table', 'drop procedure', 'create procedure'
as well as others.

mysql> CREATE USER 'yoku0825'@'localhost' IDENTIFIED BY 's3kr1t';
mysql> GRANT ALL PRIVILEGES ON test.* TO 'yoku0825'@'localhost';

You can also optionally set the user to run 'make test' with:

perl Makefile.PL --testuser=username

I will use the following settings for compiling and testing:

  cflags        (mysql_config) = -I/usr/include/mysql -m64
  ldflags       (mysql_config) =
  libs          (mysql_config) = -L/usr/lib64/mysql -lmysqlclient -lpthread -ldl -lssl -lcrypto -lresolv -lm -lrt
  mysql_config  (guessed     ) = mysql_config
  nocatchstderr (default     ) = 0
  nofoundrows   (default     ) = 0
  testdb        (default     ) = test
  testhost      (default     ) =
  testpassword  (default     ) =
  testport      (default     ) =
  testsocket    (default     ) =
  testuser      (guessed     ) = yoku0825
  version       (mysql_config) = 8.4.8

To change these settings, see 'perl Makefile.PL --help' and
'perldoc DBD::mysql::INSTALL'.

Checking if libs are available for compiling...
Looks good.

Checking if your kit is complete...
Looks good
Warning: prerequisite Test::Deep 0 not found.
Using DBI 1.641 (for perl 5.026003 on x86_64-linux-thread-multi) installed in /usr/lib64/perl5/vendor_perl/auto/DBI/
Generating a Unix-style Makefile
Writing Makefile for DBD::mysql
Writing MYMETA.yml and MYMETA.json

$ make
cp lib/DBD/mysql/INSTALL.pod blib/lib/DBD/mysql/INSTALL.pod
cp lib/DBD/mysql.pm blib/lib/DBD/mysql.pm
cp lib/DBD/mysql/GetInfo.pm blib/lib/DBD/mysql/GetInfo.pm
Running Mkbootstrap for mysql ()
chmod 644 "mysql.bs""/usr/bin/perl" -MExtUtils::Command::MM -e 'cp_nonempty' -- mysql.bs blib/arch/auto/DBD/mysql/mysql.bs 644
gcc -c  -I/usr/lib64/perl5/vendor_perl/auto/DBI -I/usr/include/mysql -m64 -g  -D_REENTRANT -D_GNU_SOURCE -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fwrapv -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g   -DVERSION=\"5.013\" -DXS_VERSION=\"5.013\" -fPIC "-I/usr/lib64/perl5/CORE"   dbdimp.c
"/usr/bin/perl" -p -e "s/~DRIVER~/mysql/g" /usr/lib64/perl5/vendor_perl/auto/DBI/Driver.xst > mysql.xsi
"/usr/bin/perl""/usr/share/perl5/vendor_perl/ExtUtils/xsubpp"  -typemap '/usr/share/perl5/ExtUtils/typemap'  mysql.xs > mysql.xsc
Warning: duplicate function definition 'do' detected in mysql.xs, line 142
Warning: duplicate function definition 'rows' detected in mysql.xs, line 550
mv mysql.xsc mysql.c
gcc -c  -I/usr/lib64/perl5/vendor_perl/auto/DBI -I/usr/include/mysql -m64 -g  -D_REENTRANT -D_GNU_SOURCE -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fwrapv -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g   -DVERSION=\"5.013\" -DXS_VERSION=\"5.013\" -fPIC "-I/usr/lib64/perl5/CORE"   mysql.c
gcc -c  -I/usr/lib64/perl5/vendor_perl/auto/DBI -I/usr/include/mysql -m64 -g  -D_REENTRANT -D_GNU_SOURCE -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fwrapv -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g   -DVERSION=\"5.013\" -DXS_VERSION=\"5.013\" -fPIC "-I/usr/lib64/perl5/CORE"   socket.c
rm -f blib/arch/auto/DBD/mysql/mysql.so
gcc  -lpthread -shared -Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -L/usr/local/lib -fstack-protector-strong  dbdimp.o mysql.o socket.o  -o blib/arch/auto/DBD/mysql/mysql.so  \
   -L/usr/lib64/mysql -lmysqlclient -lpthread -ldl -lssl -lcrypto -lresolv -lm -lrt -lperl   \

chmod 755 blib/arch/auto/DBD/mysql/mysql.so
Manifying 2 pod documents

$ exit

$ find -name mysql.so
./.cpanm/work/1769741347.2091827/DBD-mysql-5.013/blib/arch/auto/DBD/mysql/mysql.so

LD_PRELOAD.cpanmにできた mysql.soの方を使う形にして試してみる。

$ LD_PRELOAD=./.cpanm/work/1769741347.2091827/DBD-mysql-5.013/blib/arch/auto/DBD/mysql/mysql.so perl -MDBI -MData::Dumper -E 'my $conn= DBI->connect("dbi:mysql:;host=127.0.0.1;port=64080;mysql_ssl=1", "yoku0825")'

できた…
DBD::MySQLのバージョンのせいだと嫌なので念のため mariadb-connector-c-develを入れ直して試してみる。

$ perl Makefile.PL

PLEASE NOTE:

For 'make test' to run properly, you must ensure that the
database user 'yoku0825' can connect to your MySQL server
and has the proper privileges that these tests require such
as 'drop table', 'create table', 'drop procedure', 'create procedure'
as well as others.

mysql> CREATE USER 'yoku0825'@'localhost' IDENTIFIED BY 's3kr1t';
mysql> GRANT ALL PRIVILEGES ON test.* TO 'yoku0825'@'localhost';

You can also optionally set the user to run 'make test' with:

perl Makefile.PL --testuser=username

I will use the following settings for compiling and testing:

  cflags        (mysql_config) = -I/usr/include/mysql -I/usr/include/mysql/mysql
  ldflags       (mysql_config) =
  libs          (mysql_config) = -L/usr/lib64/ -lmariadb
  mysql_config  (guessed     ) = mysql_config
  nocatchstderr (default     ) = 0
  nofoundrows   (default     ) = 0
  testdb        (default     ) = test
  testhost      (default     ) =
  testpassword  (default     ) =
  testport      (default     ) =
  testsocket    (default     ) =
  testuser      (guessed     ) = yoku0825
  version       (mysql_config) = 10.5.5

To change these settings, see 'perl Makefile.PL --help' and
'perldoc DBD::mysql::INSTALL'.

Checking if libs are available for compiling...

The chosen MySQL client library appears to be MariaDB's. Compilation may fail.
Consider DBD::MariaDB or installing Oracle's MySQL client library.

Checking if your kit is complete...
Looks good
Using DBI 1.641 (for perl 5.026003 on x86_64-linux-thread-multi) installed in /usr/lib64/perl5/vendor_perl/auto/DBI/
Generating a Unix-style Makefile
Writing Makefile for DBD::mysql
Writing MYMETA.yml and MYMETA.json

$ make
cp lib/DBD/mysql/INSTALL.pod blib/lib/DBD/mysql/INSTALL.pod
cp lib/DBD/mysql.pm blib/lib/DBD/mysql.pm
cp lib/DBD/mysql/GetInfo.pm blib/lib/DBD/mysql/GetInfo.pm
Running Mkbootstrap for mysql ()
chmod 644 "mysql.bs""/usr/bin/perl" -MExtUtils::Command::MM -e 'cp_nonempty' -- mysql.bs blib/arch/auto/DBD/mysql/mysql.bs 644
gcc -c  -I/usr/lib64/perl5/vendor_perl/auto/DBI -I/usr/include/mysql -I/usr/include/mysql/mysql -g  -D_REENTRANT -D_GNU_SOURCE -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fwrapv -fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -g   -DVERSION=\"5.013\" -DXS_VERSION=\"5.013\" -fPIC "-I/usr/lib64/perl5/CORE"   dbdimp.c
dbdimp.c: In function 'parse_params':
dbdimp.c:639:22: warning: implicit declaration of function 'mysql_real_escape_string_quote'; did you mean 'mysql_real_escape_string'? [-Wimplicit-function-declaration]
               ptr += mysql_real_escape_string_quote(sock, ptr, valbuf, vallen, '\'');
                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                      mysql_real_escape_string
dbdimp.c: In function 'mysql_dr_connect':
dbdimp.c:1244:31: error: 'MYSQL_OPT_COMPRESSION_ALGORITHMS' undeclared (first use in this function); did you mean 'MYSQL_OPT_COMPRESS'?
           mysql_options(sock, MYSQL_OPT_COMPRESSION_ALGORITHMS, calg);
                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                               MYSQL_OPT_COMPRESS
dbdimp.c:1244:31: note: each undeclared identifier is reported only once for each function it appears in
dbdimp.c:1415:31: error: 'MYSQL_OPT_GET_SERVER_PUBLIC_KEY' undeclared (first use in this function); did you mean 'MYSQL_SERVER_PUBLIC_KEY'?
           mysql_options(sock, MYSQL_OPT_GET_SERVER_PUBLIC_KEY, &server_get_pubkey);
                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                               MYSQL_SERVER_PUBLIC_KEY
dbdimp.c:1479:22: error: 'SSL_MODE_PREFERRED' undeclared (first use in this function); did you mean 'SO_PEERCRED'?
           ssl_mode = SSL_MODE_PREFERRED;
                      ^~~~~~~~~~~~~~~~~~
                      SO_PEERCRED
dbdimp.c:1481:19: error: 'SSL_MODE_VERIFY_IDENTITY' undeclared (first use in this function)
        ssl_mode = SSL_MODE_VERIFY_IDENTITY;
                   ^~~~~~~~~~~~~~~~~~~~~~~~
dbdimp.c:1483:19: error: 'SSL_MODE_VERIFY_CA' undeclared (first use in this function)
        ssl_mode = SSL_MODE_VERIFY_CA;
                   ^~~~~~~~~~~~~~~~~~
dbdimp.c:1485:19: error: 'SSL_MODE_REQUIRED' undeclared (first use in this function); did you mean 'OP_REQUIRE'?
        ssl_mode = SSL_MODE_REQUIRED;
                   ^~~~~~~~~~~~~~~~~
                   OP_REQUIRE
dbdimp.c:1486:30: error: 'MYSQL_OPT_SSL_MODE' undeclared (first use in this function); did you mean 'MYSQL_OPT_SSL_CRL'?
      if (mysql_options(sock, MYSQL_OPT_SSL_MODE, &ssl_mode) != 0) {
                              ^~~~~~~~~~~~~~~~~~
                              MYSQL_OPT_SSL_CRL
dbdimp.c:1495:30: error: 'SSL_MODE_DISABLED' undeclared (first use in this function); did you mean 'SS_DISABLE'?
      unsigned int ssl_mode = SSL_MODE_DISABLED;
                              ^~~~~~~~~~~~~~~~~
                              SS_DISABLE
dbdimp.c: In function 'mysql_st_prepare':
dbdimp.c:2419:24: warning: assignment to 'my_bool *' {aka 'char *'} from incompatible pointer type '_Bool *' [-Wincompatible-pointer-types]
           bind->is_null=      &(fbind->is_null);
                        ^
dbdimp.c: In function 'mysql_st_internal_execute41':
dbdimp.c:2856:9: warning: implicit declaration of function 'mysql_stmt_bind_named_param'; did you mean 'mysql_stmt_bind_param'? [-Wimplicit-function-declaration]
     if (mysql_stmt_bind_named_param(stmt,bind,num_params, NULL))
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~
         mysql_stmt_bind_param
dbdimp.c:2915:63: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'my_ulonglong' {aka 'long long unsigned int'} [-Wformat=]
                   "\t<- mysql_internal_execute_41 returning %lu rows\n",
                                                             ~~^
                                                             %llu
                   rows);
                   ~~~~
dbdimp.c: In function 'mysql_st_execute':
dbdimp.c:3076:32: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'my_ulonglong' {aka 'long long unsigned int'} [-Wformat=]
     sprintf(actual_row_num, "%lu", imp_sth->row_num);
                              ~~^   ~~~~~~~~~~~~~~~~
                              %llu
dbdimp.c: In function 'mysql_describe':
dbdimp.c:3170:22: warning: assignment to 'my_bool *' {aka 'char *'} from incompatible pointer type '_Bool *' [-Wincompatible-pointer-types]
       buffer->is_null= &(fbh->is_null);
                      ^
dbdimp.c:3171:20: warning: assignment to 'my_bool *' {aka 'char *'} from incompatible pointer type '_Bool *' [-Wincompatible-pointer-types]
       buffer->error= (bool*) &(fbh->error);
                    ^
dbdimp.c: In function 'mysql_st_fetch':
dbdimp.c:3475:63: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'my_ulonglong' {aka 'long long unsigned int'} [-Wformat=]
       PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tmysql_num_rows=%lu\n",
                                                             ~~^
                                                             %llu
                     mysql_num_rows(imp_sth->result));
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dbdimp.c:3477:68: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'my_ulonglong' {aka 'long long unsigned int'} [-Wformat=]
       PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tmysql_affected_rows=%lu\n",
                                                                  ~~^
                                                                  %llu
                     mysql_affected_rows(imp_dbh->pmysql));
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
dbdimp.c: In function 'mysql_st_FETCH_attrib':
dbdimp.c:4081:60: warning: format '%lu' expects argument of type 'long unsigned int', but argument 3 has type 'my_ulonglong' {aka 'long long unsigned int'} [-Wformat=]
           PerlIO_printf(DBIc_LOGPIO(imp_xxh), "INSERT ID %lu\n", imp_sth->insertid);
                                                          ~~^     ~~~~~~~~~~~~~~~~~
                                                          %llu
make: *** [Makefile:352: dbdimp.o] Error 1

あっ make転けた…
じゃあDBD::MariaDB(ちゃんと存在するのだ)で試す。

$ sudo cpanm DBD::MariaDB
..

$ perl -MDBI -MData::Dumper -E 'my $conn= DBI->connect("dbi:MariaDB:;host=127.0.0.1;port=64080;mysql_ssl=1", "yoku0825")'
DBI connect(';host=127.0.0.1;port=64080;mysql_ssl=1','yoku0825',...) failed: Unknown attribute mysql_ssl at -e line 1.

今度は mysql_sslなんて知らんと言われた… mariadb_sslらしいんだけど、別にここはコンパチってわけではないのか。

$ perl -MDBI -MData::Dumper -E 'my $conn= DBI->connect("dbi:MariaDB:;host=127.0.0.1;port=64080;mariadb_ssl=1", "yoku0825")'
DBI connect(';host=127.0.0.1;port=64080;mariadb_ssl=1','yoku0825',...) failed: SSL connection error: self signed certificate in certificate chain at -e line 1.

うむ、やはりlibmariadbに依存しそう。

ただ、これがどこのコードから派生しているのかよくわからん…。

大体にして、そもそも mariadbコマンドラインクライアントは

$ ./client/mariadb -h 127.0.0.1 -P 64080 -u yoku0825 --ssl -sse "SHOW SESSION STATUS LIKE 'Ssl_cipher'"
Ssl_cipher      TLS_AES_256_GCM_SHA384

ちゃんとつながるんである!
mariadbコマンドラインクライアントが SSL connection error: self signed certificateで転けるのは --ssl-verify-server-certをつけた時だけ。

$ ./client/mariadb -h 127.0.0.1 -P 64080 -u yoku0825 --ssl --ssl-verify-server-cert -sse "SHOW SESSION STATUS LIKE 'Ssl_cipher'"
ERROR 2026 (HY000): TLS/SSL error: self signed certificate in certificate chain

じゃあDBD::mysqlも mysql_ssl_verify_server_cert=0してやればいいかと思うと

$ perl -MDBI -MData::Dumper -E 'my $conn= DBI->connect("dbi:mysql:;host=127.0.0.1;port=64080;mysql_ssl=1;mysql_ssl_verify_server_cert=0", "yoku0825")'
DBI connect(';host=127.0.0.1;port=64080;mysql_ssl=1;mysql_ssl_verify_server_cert=0','yoku0825',...) failed: SSL connection error: Enforcing SSL encryption is not supported without mysql_ssl_verify_server_cert at -e line 1.

今度は Enforcing SSL encryption is not supported without mysql_ssl_verify_server_certで転けるのである…。

libmysqlclientにリンクしたDBD::mysqlならこっちも文句は言わない。

$ LD_PRELOAD=~/.cpanm/work/1769741347.2091827/DBD-mysql-5.013/blib/arch/auto/DBD/mysql/mysql.so perl -MDBI -MData::Dumper -E 'my $conn= DBI->connect("dbi:mysql:;host=127.0.0.1;port=64080;mysql_ssl=1;mysql_ssl_verify_server_cert=0", "yoku0825")'

わからない…。


ALTER TABLE .. ADD KEY .. がERROR 1878 (HY000): Temporary file write failure. で失敗する

$
0
0

TL;DR

  • なんか見慣れない感じがするのは ALTER TABLE .. ADD INDEX .., ALGORITHM = INPLACE (Fast Index Creation)の時に起こるエラーだから
  • innodb_tmpdirの容量が足りないと起こる
  • なので他に容量があるなら innodb_tmpdirの場所を変えるか、 tmpdirの場所を変える ( innodb_tmpdirに値を設定しなかった場合は tmpdirの値がコピーされるから)
  • ストレージ容量の問題なので、 innodb_sort_buffer_size の値を変えたところで意味はなさそう

datadirに /tmp/datadir , tmpdirに /tmp/tmpdirを指定してmysqldを起動する。この時、 /tmp/tmpdirが100MBしか使えない状態にしておいた。

$ dd if=/dev/zero of=~/100mb bs=1M count=100
$ mkfs -t xfs ~/100mb
$ mkdir -p /tmp/tmpdir
$ sudo mount ~/100mb /tmp/tmpdir
$ sudo chown yoku0825. /tmp/tmpdir

$ /usr/mysql/8.4.8/bin/mysqld --no-defaults --datadir=/tmp/datadir --initialize-insecure
$ /usr/mysql/8.4.8/bin/mysqld --no-defaults --datadir=/tmp/datadir --tmpdir=/tmp/tmpdir --daemonize --innodb-buffer_pool_size=10G --local-infile --log-error-verbosity=3

$ df -h /tmp/datadir /tmp/tmpdir/
Filesystem                  Size  Used Avail Use% Mounted on
/dev/mapper/ocivolume-root   36G   24G   12G  67% /
/dev/loop0                   95M  6.0M   89M   7% /tmp/tmpdir

ダミーデータを突っ込む。

mysql> CREATE DATABASE d1;
Query OK, 1 row affected (0.00 sec)

mysql> CREATE TABLE d1.t1 (num SERIAL, val VARCHAR(32));
Query OK, 0 rows affected (0.02 sec)

mysql> SET SESSION sql_log_bin = OFF;
Query OK, 0 rows affected (0.00 sec)

mysql> ALTER INSTANCE DISABLE INNODB REDO_LOG;
Query OK, 0 rows affected (0.01 sec)

mysql> LOAD DATA LOCAL INFILE '/usr/mysql/md5_10000000' INTO TABLE d1.t1;
Query OK, 10000000 rows affected (29.93 sec)
Records: 10000000  Deleted: 0  Skipped: 0  Warnings: 0

mysql> ALTER INSTANCE ENABLE INNODB REDO_LOG;
Query OK, 0 rows affected (4.32 sec)

mysql> SET SESSION sql_log_bin = ON;
Query OK, 0 rows affected (0.00 sec)

ALTER TABLE ( CREATE INDEXでもいい ) でFast Index Creationを起こして ERROR 1878 (HY000): Temporary file write failure.を起こす。

mysql> SELECT @@tmpdir, @@innodb_tmpdir;
+-------------+-----------------+
| @@tmpdir    | @@innodb_tmpdir |
+-------------+-----------------+
| /tmp/tmpdir | NULL            |
+-------------+-----------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE d1.t1 ADD KEY (val);
ERROR 1878 (HY000): Temporary file write failure.

$ cat /tmp/datadir/yoku0825-sandbox.err
..
2026-03-01T09:47:53.068413Z 0 [Warning] [MY-012637] [InnoDB] 61440 bytes should have been written. Only 16384 bytes written. Retrying for the remaining bytes.
2026-03-01T09:47:54.401197Z 0 [Warning] [MY-012637] [InnoDB] 61440 bytes should have been written. Only 16384 bytes written. Retrying for the remaining bytes.
2026-03-01T09:47:54.403201Z 0 [Warning] [MY-012638] [InnoDB] Retry attempts for writing partial data failed.
2026-03-01T09:47:54.403226Z 0 [ERROR] [MY-012639] [InnoDB] Write to file (ddl) failed at offset 25559040, 61440 bytes should have been written, only 0 were written. Operating system error number 28. Check that your OS and file system support files of this size. Check also that the disk is not full or a disk quota exceeded.
2026-03-01T09:47:54.403245Z 0 [ERROR] [MY-012640] [InnoDB] Error number 28 means 'No space left on device'
2026-03-01T09:47:54.403252Z 0 [Note] [MY-012641] [InnoDB] Refer to your operating system documentation for operating system error code information.
2026-03-01T09:47:54.404041Z 0 [Warning] [MY-012638] [InnoDB] Retry attempts for writing partial data failed.

innodb_tmpdirを別の場所(今回は100MB制限をしてない /tmp)に移す。

mysql> SHUTDOWN;
Query OK, 0 rows affected (0.00 sec)

$ /usr/mysql/8.4.8/bin/mysqld --no-defaults --datadir=/tmp/datadir --tmpdir=/tmp/tmpdir --daemonize --innodb-buffer_pool_size=10G --local-infile --log-error-verbosity=3 --innodb_tmpdir=/tmp

mysql> SELECT @@tmpdir, @@innodb_tmpdir;
+-------------+-----------------+
| @@tmpdir    | @@innodb_tmpdir |
+-------------+-----------------+
| /tmp/tmpdir | /tmp            |
+-------------+-----------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE d1.t1 ADD KEY (val);
Query OK, 0 rows affected (2 min 22.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

成功した。
innodb_tmpdirを未指定にして、 tmpdir/tmpに移す。

mysql> ALTER TABLE d1.t1 DROP KEY val;
Query OK, 0 rows affected (0.08 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SHUTDOWN;
Query OK, 0 rows affected (0.00 sec)

$ /usr/mysql/8.4.8/bin/mysqld --no-defaults --datadir=/tmp/datadir --tmpdir=/tmp/tmpdir --daemonize --innodb-buffer_pool_size=10G --local-infile --log-error-verbosity=3 --tmpdir=/tmp

mysql> SELECT @@tmpdir, @@innodb_tmpdir;
+----------+-----------------+
| @@tmpdir | @@innodb_tmpdir |
+----------+-----------------+
| /tmp     | NULL            |
+----------+-----------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE d1.t1 ADD KEY (val);
Query OK, 0 rows affected (2 min 18.33 sec)
Records: 0  Duplicates: 0  Warnings: 0

成功する。
最後に tmpdirinnodb_tmpdirもサイズ制限がある状態に戻して、オプションを足してテーブルリビルドにすればいけるか試した。

mysql> ALTER TABLE d1.t1 DROP KEY val;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SHUTDOWN;
Query OK, 0 rows affected (0.00 sec)

$ /usr/mysql/8.4.8/bin/mysqld --no-defaults --datadir=/tmp/datadir --tmpdir=/tmp/tmpdir --daemonize --innodb-buffer_pool_size=10G --local-infile --log-error-verbosity=3

mysql> SELECT @@tmpdir, @@innodb_tmpdir;
+-------------+-----------------+
| @@tmpdir    | @@innodb_tmpdir |
+-------------+-----------------+
| /tmp/tmpdir | NULL            |
+-------------+-----------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE d1.t1 ADD KEY (val);  -- 何もしなければ失敗する
ERROR 1878 (HY000): Temporary file write failure.

mysql> ALTER TABLE d1.t1 ADD KEY (val), Engine = InnoDB;  -- Engine = InnoDBを付けても実オペレーションのADD KEYがあるからテーブルリビルドになってくれない
ERROR 1878 (HY000): Temporary file write failure.

mysql> ALTER TABLE d1.t1 ADD KEY (val), FORCE;  -- FORCEをつけても同上
ERROR 1878 (HY000): Temporary file write failure.

mysql> ALTER TABLE d1.t1 ADD KEY (val), ALGORITHM = COPY;  -- ALGORITHM=COPYならさすがにテーブルコピーなので通った

Query OK, 10000000 rows affected (7 min 53.70 sec)
Records: 10000000  Duplicates: 0  Warnings: 0

↓確かにHash Join周りでも出そう(まだ遭遇したことはない)

$ perror 1878
MySQL error code MY-001878 (ER_TEMP_FILE_WRITE_FAILURE): Temporary file write failure.

$ global -g ER_TEMP_FILE_WRITE_FAILURE
include/mysqld_ername.h
include/mysqld_errmsg.h
include/mysqld_error.h
sql/handler.cc
sql/iterators/composite_iterators.cc
sql/iterators/hash_join_chunk.cc
sql/iterators/hash_join_iterator.cc
storage/innobase/handler/handler0alter.cc

InnoDB FULLTEXT KEYがメモリを食う件(未解決)

$
0
0

準備

$ sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_common --table_size=1000000 prepare

mysql80 30> SELECT format_bytes(@@innodb_buffer_pool_size);
+-----------------------------------------+
| format_bytes(@@innodb_buffer_pool_size) |
+-----------------------------------------+
| 64.00 MiB                               |
+-----------------------------------------+
1 row in set (0.00 sec)

mysql80 30> SHOW ENGINE PERFORMANCE_SCHEMA STATUS;

..
| performance_schema | performance_schema.memory                                   | 240987688 |
+--------------------+-------------------------------------------------------------+-----------+
248 rows in set (0.00 sec)

$ ll -h /usr/mysql/8.0.45/data/sbtest/sbtest1.ibd
-rw-r-----. 1 yoku0825 yoku0825 248M Mar  9 06:03 /usr/mysql/8.0.45/data/sbtest/sbtest1.ibd

まずはそのままrun

$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date
Mon Mar  9 05:59:57 GMT 2026
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 1 tps: 1166.75 qps: 7002.48 (r/w/o: 0.00/4667.99/2334.49) lat (ms,95%): 1.70 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 1 tps: 1155.29 qps: 6930.74 (r/w/o: 0.00/4620.16/2310.58) lat (ms,95%): 1.89 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 1 tps: 1182.97 qps: 7096.83 (r/w/o: 0.00/4731.89/2364.94) lat (ms,95%): 1.76 err/s: 0.00 reconn/s: 0.00
[ 4s ] thds: 1 tps: 1174.02 qps: 7046.14 (r/w/o: 0.00/4697.09/2349.05) lat (ms,95%): 1.82 err/s: 0.00 reconn/s: 0.00

..
[ 597s ] thds: 1 tps: 1167.86 qps: 7008.16 (r/w/o: 0.00/4672.44/2335.72) lat (ms,95%): 1.93 err/s: 0.00 reconn/s: 0.00
[ 598s ] thds: 1 tps: 1180.12 qps: 7082.71 (r/w/o: 0.00/4722.47/2360.24) lat (ms,95%): 1.86 err/s: 0.00 reconn/s: 0.00
[ 599s ] thds: 1 tps: 1194.96 qps: 7168.75 (r/w/o: 0.00/4778.84/2389.92) lat (ms,95%): 1.79 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            0
        write:                           2791284
        other:                           1395642
        total:                           4186926
    transactions:                        697821 (1163.03 per sec.)
    queries:                             4186926 (6978.17 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          600.0019s
    total number of events:              697821

Latency (ms):
         min:                                    0.31
         avg:                                    0.86
         max:                                   61.44
         95th percentile:                        1.86
         sum:                               599120.68

Threads fairness:
    events (avg/stddev):           697821.0000/0.00
    execution time (avg/stddev):   599.1207/0.00

Mon Mar  9 06:09:57 GMT 2026

$ while true ; do ps -Ao pid,fname,rss | grep $(pidof mysqld) | tsadd | tee -a /tmp/mysql8045_rss.txt; sleep 1; done
..
[2026/03/09 05:59:55]  1025869 mysqld   451000
[2026/03/09 05:59:56]  1025869 mysqld   451260
[2026/03/09 05:59:57]  1025869 mysqld   454952
[2026/03/09 05:59:58]  1025869 mysqld   472896
[2026/03/09 05:59:59]  1025869 mysqld   474364
[2026/03/09 06:00:00]  1025869 mysqld   474364

..
[2026/03/09 06:09:50]  1025869 mysqld   479124
[2026/03/09 06:09:51]  1025869 mysqld   479124
[2026/03/09 06:09:53]  1025869 mysqld   479124
[2026/03/09 06:09:54]  1025869 mysqld   479124
[2026/03/09 06:09:55]  1025869 mysqld   479124
[2026/03/09 06:09:56]  1025869 mysqld   479124
[2026/03/09 06:09:57]  1025869 mysqld   479124
[2026/03/09 06:09:58]  1025869 mysqld   479124
[2026/03/09 06:09:59]  1025869 mysqld   479124
[2026/03/09 06:10:00]  1025869 mysqld   479124
[2026/03/09 06:10:01]  1025869 mysqld   479124
[2026/03/09 06:10:02]  1025869 mysqld   479036

次、これにそのままFULLTEXT INDEXを貼ってリスタート

mysql80 55> ALTER TABLE sbtest.sbtest1 ADD FULLTEXT KEY (c);
Query OK, 0 rows affected, 1 warning (1 min 37.83 sec)
Records: 0  Duplicates: 0  Warnings: 1

mysql80 55> SHOW WARNINGS;
+---------+------+--------------------------------------------------+
| Level   | Code | Message                                          |
+---------+------+--------------------------------------------------+
| Warning |  124 | InnoDB rebuilding table to add column FTS_DOC_ID |
+---------+------+--------------------------------------------------+
1 row in set (0.00 sec)

mysql80 55> SELECT @@innodb_buffer_pool_load_at_startup;
+--------------------------------------+
| @@innodb_buffer_pool_load_at_startup |
+--------------------------------------+
|                                    0 |
+--------------------------------------+
1 row in set (0.00 sec)

mysql80 55> RESTART;
Query OK, 0 rows affected (0.01 sec)

で、sysbench run。1スレッドだというのにtpsが0になる瞬間が度々ある。。この時点で嫌な予感しかしない。。

$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date
Mon Mar  9 06:14:07 GMT 2026
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 1 tps: 415.17 qps: 2493.00 (r/w/o: 0.00/1661.67/831.33) lat (ms,95%): 4.91 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 1 tps: 375.14 qps: 2251.86 (r/w/o: 0.00/1501.57/750.29) lat (ms,95%): 5.18 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 1 tps: 375.97 qps: 2254.82 (r/w/o: 0.00/1502.88/751.94) lat (ms,95%): 4.91 err/s: 0.00 reconn/s: 0.00

..
[ 69s ] thds: 1 tps: 309.01 qps: 1856.04 (r/w/o: 0.00/1238.03/618.01) lat (ms,95%): 6.67 err/s: 0.00 reconn/s: 0.00
[ 70s ] thds: 1 tps: 330.00 qps: 1979.98 (r/w/o: 0.00/1319.99/659.99) lat (ms,95%): 5.28 err/s: 0.00 reconn/s: 0.00
[ 71s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 72s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 73s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 74s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00

..
[ 98s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 99s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 100s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 101s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 102s ] thds: 1 tps: 19.00 qps: 113.00 (r/w/o: 0.00/75.00/38.00) lat (ms,95%): 8.58 err/s: 0.00 reconn/s: 0.00
[ 103s ] thds: 1 tps: 311.99 qps: 1868.95 (r/w/o: 0.00/1244.97/623.98) lat (ms,95%): 5.88 err/s: 0.00 reconn/s: 0.00
[ 104s ] thds: 1 tps: 353.97 qps: 2123.83 (r/w/o: 0.00/1415.89/707.94) lat (ms,95%): 5.67 err/s: 0.00 reconn/s: 0.00
[ 105s ] thds: 1 tps: 360.04 qps: 2160.25 (r/w/o: 0.00/1440.17/720.08) lat (ms,95%): 5.47 err/s: 0.00 reconn/s: 0.00

..
[ 647s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 648s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 649s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 650s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            0
        write:                           208020
        other:                           104010
        total:                           312030
    transactions:                        52005  (79.93 per sec.)
    queries:                             312030 (479.56 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          650.6557s
    total number of events:              52005

Latency (ms):
         min:                                    0.46
         avg:                                   12.51
         max:                                58936.07
         95th percentile:                        7.17
         sum:                               650556.58

Threads fairness:
    events (avg/stddev):           52005.0000/0.00
    execution time (avg/stddev):   650.5566/0.00

Mon Mar  9 06:24:58 GMT 2026

$ while true ; do ps -Ao pid,fname,rss | grep $(pidof mysqld) | tsadd | tee -a /tmp/mysql8045_rss.txt; sleep 1; done
..
[2026/03/09 06:14:02]  1036381 mysqld   403772
[2026/03/09 06:14:03]  1036381 mysqld   403772
[2026/03/09 06:14:04]  1036381 mysqld   403772
[2026/03/09 06:14:05]  1036381 mysqld   403772
[2026/03/09 06:14:06]  1036381 mysqld   405136
[2026/03/09 06:14:07]  1036381 mysqld   405136
[2026/03/09 06:14:08]  1036381 mysqld   423324
[2026/03/09 06:14:09]  1036381 mysqld   468492
[2026/03/09 06:14:10]  1036381 mysqld   471908

..
[2026/03/09 06:24:53]  1036381 mysqld   562712
[2026/03/09 06:24:54]  1036381 mysqld   562712
[2026/03/09 06:24:55]  1036381 mysqld   562712
[2026/03/09 06:24:56]  1036381 mysqld   562712
[2026/03/09 06:24:57]  1036381 mysqld   562712
[2026/03/09 06:24:58]  1036381 mysqld   562712
[2026/03/09 06:24:59]  1036381 mysqld   562712
[2026/03/09 06:25:00]  1036381 mysqld   562712
[2026/03/09 06:25:01]  1036381 mysqld   562712
[2026/03/09 06:25:02]  1036381 mysqld   562712
[2026/03/09 06:25:03]  1036381 mysqld   562712
[2026/03/09 06:25:04]  1036381 mysqld   562712
[2026/03/09 06:25:05]  1036381 mysqld   562712
[2026/03/09 06:25:06]  1036381 mysqld   562712

遅いのは良いとして(InnoDB Fulltext Indexの仕組み上書き込みするファイルが莫大に増えるので)、 FULLTEXT INDEXを貼っただけで想像以上にQPSも安定しないしない場合に比べて100MB近くも余計にメモリを使っている。
(100MB弱というとインパクトは小さそうだが、バッファプールの1.5倍と考えると結構恐怖である)
なお oltp_write_onlyなので、実行されているのは下記の4パターンだけ。

# Time: 2026-03-09T06:23:59.708497-00:00
# User@Host: root[root] @ localhost []  Id:    12
# Query_time: 0.000088  Lock_time: 0.000001 Rows_sent: 0  Rows_examined: 1 Thread_id: 12 Errno: 0 Killed: 0 Bytes_received: 24 Bytes_sent: 52 Read_first: 0 Read_last: 0 Read_key: 1 Read_next: 0 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: 2026-03-09T06:23:59.708409-00:00 End: 2026-03-09T06:23:59.708497-00:00
SET timestamp=1773037439;
UPDATE sbtest1 SET k=k+1 WHERE id=504014;
# Time: 2026-03-09T06:23:59.708599-00:00
# User@Host: root[root] @ localhost []  Id:    12
# Query_time: 0.000086  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 1 Thread_id: 12 Errno: 0 Killed: 0 Bytes_received: 144 Bytes_sent: 52 Read_first: 0 Read_last: 0 Read_key: 1 Read_next: 0 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: 2026-03-09T06:23:59.708513-00:00 End: 2026-03-09T06:23:59.708599-00:00
SET timestamp=1773037439;
UPDATE sbtest1 SET c='88212461572-85833769425-67430101277-82161871917-42390199524-62707776009-18076322187-41876970240-69115335595-77733484768' WHERE id=499195;
# Time: 2026-03-09T06:23:59.709583-00:00
# User@Host: root[root] @ localhost []  Id:    12
# Query_time: 0.000965  Lock_time: 0.000001 Rows_sent: 0  Rows_examined: 1 Thread_id: 12 Errno: 0 Killed: 0 Bytes_received: 24 Bytes_sent: 11 Read_first: 0 Read_last: 0 Read_key: 1 Read_next: 0 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: 2026-03-09T06:23:59.708618-00:00 End: 2026-03-09T06:23:59.709583-00:00
SET timestamp=1773037439;
DELETE FROM sbtest1 WHERE id=526199;
# Time: 2026-03-09T06:23:59.709686-00:00
# User@Host: root[root] @ localhost []  Id:    12
# Query_time: 0.000080  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 0 Thread_id: 12 Errno: 0 Killed: 0 Bytes_received: 212 Bytes_sent: 14 Read_first: 0 Read_last: 0 Read_key: 0 Read_next: 0 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: 2026-03-09T06:23:59.709606-00:00 End: 2026-03-09T06:23:59.709686-00:00
SET timestamp=1773037439;
INSERT INTO sbtest1 (id, k, c, pad) VALUES (526199, 499060, '94977596404-90535763498-48349239206-18885582465-18756331065-84602218017-85305678009-81368039801-39635475206-86387067089', '54494238140-99187086479-72783598041-89209528431-78795269286');

さて、このメモリの増分をパラメータだけで何とかしたい。
取り敢えず一度取っ散らかった(はずの)Fulltext Indexを一度OPTIMIZEで綺麗にしてRESTART。

mysql80 12> OPTIMIZE TABLE sbtest.sbtest1;

+----------------+----------+----------+-------------------------------------------------------------------+
| Table          | Op       | Msg_type | Msg_text                                                          |
+----------------+----------+----------+-------------------------------------------------------------------+
| sbtest.sbtest1 | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| sbtest.sbtest1 | optimize | status   | OK                                                                |
+----------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (1 hour 27 min 51.56 sec)

mysql80 12> RESTART;
Query OK, 0 rows affected (0.00 sec)

mysql80 12> SHOW GLOBAL VARIABLES LIKE 'innodb\_ft%\_size';
+----------------------------+-----------+
| Variable_name              | Value     |
+----------------------------+-----------+
| innodb_ft_cache_size       | 8000000   |
| innodb_ft_max_token_size   | 84        |
| innodb_ft_min_token_size   | 3         |
| innodb_ft_total_cache_size | 640000000 |
+----------------------------+-----------+
4 rows in set (0.00 sec)

innodb_ft_total_cache_sizeで抑えられるかなと思って変更しようとしたらread_onlyだった。

mysql80 12> SET GLOBAL innodb_ft_total_cache_size = 32000000;
ERROR 1238 (HY000): Variable 'innodb_ft_total_cache_size' is a read only variable

mysql80 12> SET PERSIST_ONLY innodb_ft_total_cache_size = 32000000;
Query OK, 0 rows affected (0.00 sec)

mysql80 12> RESTART;
Query OK, 0 rows affected (0.00 sec)

PERSIST_ONLYが効くならSQLだけで済むのは良い時代になったものだ…

mysql80 12> SHOW GLOBAL VARIABLES LIKE 'innodb\_ft%\_size';
ERROR 2013 (HY000): Lost connection to MySQL server during query
No connection. Trying to reconnect...
Connection id:    12
Current database: *** NONE ***

+----------------------------+----------+
| Variable_name              | Value    |
+----------------------------+----------+
| innodb_ft_cache_size       | 8000000  |
| innodb_ft_max_token_size   | 84       |
| innodb_ft_min_token_size   | 3        |
| innodb_ft_total_cache_size | 32000000 |
+----------------------------+----------+
4 rows in set (0.01 sec)

これでsysbench run

$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date
Mon Mar  9 08:12:30 GMT 2026
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 1 tps: 801.57 qps: 4810.42 (r/w/o: 0.00/3206.28/1604.14) lat (ms,95%): 2.35 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 1 tps: 701.09 qps: 4210.56 (r/w/o: 0.00/2808.37/1402.19) lat (ms,95%): 3.13 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 1 tps: 660.27 qps: 3958.63 (r/w/o: 0.00/2638.09/1320.54) lat (ms,95%): 3.19 err/s: 0.00 reconn/s: 0.00

..
[ 655s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 656s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
[ 657s ] thds: 1 tps: 0.00 qps: 0.00 (r/w/o: 0.00/0.00/0.00) lat (ms,95%): 0.00 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            0
        write:                           355052
        other:                           177526
        total:                           532578
    transactions:                        88763  (134.99 per sec.)
    queries:                             532578 (809.93 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          657.5574s
    total number of events:              88763

Latency (ms):
         min:                                    0.47
         avg:                                    7.41
         max:                                62161.87
         95th percentile:                        4.74
         sum:                               657402.59

Threads fairness:
    events (avg/stddev):           88763.0000/0.00
    execution time (avg/stddev):   657.4026/0.00

Mon Mar  9 08:23:28 GMT 2026

[2026/03/09 08:12:25]  1115433 mysqld   411844
[2026/03/09 08:12:26]  1115433 mysqld   411844
[2026/03/09 08:12:27]  1115433 mysqld   411844
[2026/03/09 08:12:28]  1115433 mysqld   411844
[2026/03/09 08:12:29]  1115433 mysqld   411844
[2026/03/09 08:12:30]  1115433 mysqld   411844
[2026/03/09 08:12:31]  1115433 mysqld   472512
[2026/03/09 08:12:32]  1115433 mysqld   485548
[2026/03/09 08:12:33]  1115433 mysqld   491852
[2026/03/09 08:12:34]  1115433 mysqld   497860
[2026/03/09 08:12:35]  1115433 mysqld   503836
[2026/03/09 08:12:36]  1115433 mysqld   509548
[2026/03/09 08:12:37]  1115433 mysqld   514148
[2026/03/09 08:12:38]  1115433 mysqld   520156
[2026/03/09 08:12:39]  1115433 mysqld   525948

..
[2026/03/09 08:23:23]  1115433 mysqld   658016
[2026/03/09 08:23:24]  1115433 mysqld   658016
[2026/03/09 08:23:25]  1115433 mysqld   658016
[2026/03/09 08:23:26]  1115433 mysqld   658016
[2026/03/09 08:23:27]  1115433 mysqld   658016
[2026/03/09 08:23:28]  1115433 mysqld   658016
[2026/03/09 08:23:29]  1115433 mysqld   658016
[2026/03/09 08:23:30]  1115433 mysqld   658016
[2026/03/09 08:23:31]  1115433 mysqld   658016
[2026/03/09 08:23:32]  1115433 mysqld   658016
[2026/03/09 08:23:33]  1115433 mysqld   658016

sysbenchのtpsが0に張り付く時間が増えた…(でもスループットは上がってるみたいなんだよな)安定しなくなる上にメモリ使用量もむしろ増えているのでボツ。

mysql80 59> RESET PERSIST innodb_ft_total_cache_size;
Query OK, 0 rows affected (0.00 sec)

mysql80 59> OPTIMIZE TABLE sbtest.sbtest1;
+----------------+----------+----------+-------------------------------------------------------------------+
| Table          | Op       | Msg_type | Msg_text                                                          |
+----------------+----------+----------+-------------------------------------------------------------------+
| sbtest.sbtest1 | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| sbtest.sbtest1 | optimize | status   | OK                                                                |
+----------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (1 hour 27 min 55.98 sec)

mysql80 59> RESTART;

Query OK, 0 rows affected (0.00 sec)

次は innodb_ft_cache_size…と思ったらコイツも再起動が必要。

mysql80 12> SET PERSIST_ONLY innodb_ft_cache_size = 1600000;
Query OK, 0 rows affected (0.01 sec)

mysql80 12> RESTART;
Query OK, 0 rows affected (0.00 sec)

mysql80 12> SHOW GLOBAL VARIABLES LIKE 'innodb\_ft%\_size';
+----------------------------+-----------+
| Variable_name              | Value     |
+----------------------------+-----------+
| innodb_ft_cache_size       | 1600000   |
| innodb_ft_max_token_size   | 84        |
| innodb_ft_min_token_size   | 3         |
| innodb_ft_total_cache_size | 640000000 |
+----------------------------+-----------+
4 rows in set (0.01 sec)

で、sysbench run

$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date
Tue Mar 10 01:28:36 GMT 2026
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 1 tps: 775.59 qps: 4653.52 (r/w/o: 0.00/3102.35/1551.17) lat (ms,95%): 2.43 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 1 tps: 556.13 qps: 3338.79 (r/w/o: 0.00/2225.53/1113.26) lat (ms,95%): 3.75 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 1 tps: 527.97 qps: 3167.83 (r/w/o: 0.00/2111.89/1055.94) lat (ms,95%): 3.96 err/s: 0.00 reconn/s: 0.00

...
[ 597s ] thds: 1 tps: 456.97 qps: 2745.84 (r/w/o: 0.00/1831.90/913.95) lat (ms,95%): 4.57 err/s: 0.00 reconn/s: 0.00
[ 598s ] thds: 1 tps: 445.99 qps: 2675.94 (r/w/o: 0.00/1783.96/891.98) lat (ms,95%): 4.91 err/s: 0.00 reconn/s: 0.00
[ 599s ] thds: 1 tps: 457.00 qps: 2741.03 (r/w/o: 0.00/1827.02/914.01) lat (ms,95%): 4.82 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            0
        write:                           268980
        other:                           134490
        total:                           403470
    transactions:                        67245  (112.07 per sec.)
    queries:                             403470 (672.45 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          600.0019s
    total number of events:              67245

Latency (ms):
         min:                                    0.48
         avg:                                    8.92
         max:                                56495.87
         95th percentile:                        4.57
         sum:                               599877.71

Threads fairness:
    events (avg/stddev):           67245.0000/0.00
    execution time (avg/stddev):   599.8777/0.00

Tue Mar 10 01:38:36 GMT 2026

[2026/03/10 01:28:33]  1825300 mysqld   407444
[2026/03/10 01:28:34]  1825300 mysqld   407444
[2026/03/10 01:28:35]  1825300 mysqld   407444
[2026/03/10 01:28:36]  1825300 mysqld   407708
[2026/03/10 01:28:37]  1825300 mysqld   471732
[2026/03/10 01:28:38]  1825300 mysqld   479340
[2026/03/10 01:28:39]  1825300 mysqld   484292

..
[2026/03/10 01:38:31]  1825300 mysqld   597808
[2026/03/10 01:38:32]  1825300 mysqld   597808
[2026/03/10 01:38:33]  1825300 mysqld   597808
[2026/03/10 01:38:34]  1825300 mysqld   597808
[2026/03/10 01:38:35]  1825300 mysqld   597808
[2026/03/10 01:38:36]  1825300 mysqld   597808
[2026/03/10 01:38:37]  1825300 mysqld   597808
[2026/03/10 01:38:38]  1825300 mysqld   597808
[2026/03/10 01:38:39]  1825300 mysqld   597808
[2026/03/10 01:38:40]  1825300 mysqld   597808

これも効果なさそう。

mysql80 995> OPTIMIZE TABLE sbtest.sbtest1;
+----------------+----------+----------+-------------------------------------------------------------------+
| Table          | Op       | Msg_type | Msg_text                                                          |
+----------------+----------+----------+-------------------------------------------------------------------+
| sbtest.sbtest1 | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| sbtest.sbtest1 | optimize | status   | OK                                                                |
+----------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (1 hour 26 min 29.82 sec)

mysql80 995> RESET PERSIST innodb_ft_cache_size ;
Query OK, 0 rows affected (0.00 sec)

mysql80 995> RESTART;
Query OK, 0 rows affected (0.00 sec)

流石に innodb_ft_result_cache_limitは効果ないだろうと思いつつ一応(だって全文検索していないわけだし)

mysql80 12> SET GLOBAL innodb_ft_result_cache_limit = 1000000;
Query OK, 0 rows affected (0.00 sec)

$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date
Tue Mar 10 08:24:37 GMT 2026
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 1 tps: 785.45 qps: 4713.68 (r/w/o: 0.00/3141.79/1571.89) lat (ms,95%): 2.39 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 1 tps: 663.15 qps: 3979.93 (r/w/o: 0.00/2653.62/1326.31) lat (ms,95%): 3.25 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 1 tps: 631.00 qps: 3788.98 (r/w/o: 0.00/2526.99/1261.99) lat (ms,95%): 3.30 err/s: 0.00 reconn/s: 0.00

..
[ 597s ] thds: 1 tps: 372.00 qps: 2230.00 (r/w/o: 0.00/1486.00/744.00) lat (ms,95%): 5.28 err/s: 0.00 reconn/s: 0.00
[ 598s ] thds: 1 tps: 364.99 qps: 2193.92 (r/w/o: 0.00/1463.95/729.97) lat (ms,95%): 5.28 err/s: 0.00 reconn/s: 0.00
[ 599s ] thds: 1 tps: 368.02 qps: 2208.11 (r/w/o: 0.00/1472.07/736.04) lat (ms,95%): 5.47 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            0
        write:                           342848
        other:                           171424
        total:                           514272
    transactions:                        85712  (142.85 per sec.)
    queries:                             514272 (857.12 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          600.0019s
    total number of events:              85712

Latency (ms):
         min:                                    0.49
         avg:                                    7.00
         max:                                61040.07
         95th percentile:                        4.82
         sum:                               599845.92

Threads fairness:
    events (avg/stddev):           85712.0000/0.00
    execution time (avg/stddev):   599.8459/0.00

Tue Mar 10 08:34:37 GMT 2026

[2026/03/10 08:24:32]  2106989 mysqld   409968
[2026/03/10 08:24:33]  2106989 mysqld   409968
[2026/03/10 08:24:34]  2106989 mysqld   409968
[2026/03/10 08:24:35]  2106989 mysqld   409968
[2026/03/10 08:24:36]  2106989 mysqld   409968
[2026/03/10 08:24:37]  2106989 mysqld   466576
[2026/03/10 08:24:38]  2106989 mysqld   482332
[2026/03/10 08:24:39]  2106989 mysqld   488388
[2026/03/10 08:24:40]  2106989 mysqld   494672
[2026/03/10 08:24:41]  2106989 mysqld   500664
[2026/03/10 08:24:42]  2106989 mysqld   506408
[2026/03/10 08:24:44]  2106989 mysqld   512168

..
[2026/03/10 08:34:33]  2106989 mysqld   639492                                                                                        
[2026/03/10 08:34:34]  2106989 mysqld   639492
[2026/03/10 08:34:35]  2106989 mysqld   639492
[2026/03/10 08:34:36]  2106989 mysqld   639492
[2026/03/10 08:34:37]  2106989 mysqld   639492
[2026/03/10 08:34:38]  2106989 mysqld   639492
[2026/03/10 08:34:39]  2106989 mysqld   639492
[2026/03/10 08:34:40]  2106989 mysqld   639492

Σ(゚д゚lll) 何を減らしてもメモリ使用量が増えてる気がする


【2026/04/06 16:32】

続き

日々の覚書: InnoDB FTSがメモリを食う件その2 tcmallocでいくらかマシになりそう

MySQL 9.6.0とMySQL 9.7.0-er 公式ビルド済みバイナリの測り比べ

$
0
0

MySQL 9.7.0-erではPGO(Profile Guided Optimization)が有効になっているらしいので、それまでのバイナリよりもちょっと性能が良さそう。あと、これはビルドの中で有効にするので手でビルドしたものよりもちょっと性能が良いはず。

というわけで雑ベンチ。


$ cat /etc/os-release

NAME="Oracle Linux Server"

VERSION="8.10"

ID="ol"

ID_LIKE="fedora"

VARIANT="Server"

VARIANT_ID="server"

VERSION_ID="8.10"

PLATFORM_ID="platform:el8"

PRETTY_NAME="Oracle Linux Server 8.10"

ANSI_COLOR="0;31"

CPE_NAME="cpe:/o:oracle:linux:8:10:server"

HOME_URL="https://linux.oracle.com/"

BUG_REPORT_URL="https://github.com/oracle/oracle-linux"

ORACLE_BUGZILLA_PRODUCT="Oracle Linux 8"

ORACLE_BUGZILLA_PRODUCT_VERSION=8.10

ORACLE_SUPPORT_PRODUCT="Oracle Linux"

ORACLE_SUPPORT_PRODUCT_VERSION=8.10

$ nproc
4

$ free -h
              total        used        free      shared  buff/cache   available
Mem:           15Gi       2.2Gi       376Mi       682Mi        12Gi        12Gi
Swap:         4.0Gi       318Mi       3.7Gi

MySQL 9.6.0のビルド済みバイナリ。 innodb_dedicated_serverに設定は任せてしまうことにした。

$ wget https://dev.mysql.com/get/Downloads/MySQL-9.6/mysql-9.6.0-linux-glibc2.28-x86_64.tar.xz
$ tar xf mysql-9.6.0-linux-glibc2.28-x86_64.tar.xz

$ cd mysql-9.6.0-linux-glibc2.28-x86_64/
$ bin/mysqld --no-defaults --initialize-insecure --lower_case_table_names=1
$ bin/mysqld --no-defaults --lower_case_table_names=1 --innodb-dedicated-server=1 --log-error-verbosity=3 --innodb-monitor-enable=all --daemonize

ベンチマーククライアント側は雑にサーバー側の倍のスレッドになるように。100万行10テーブルでサーバ側のサイズは2.4GB。

$ nproc
8

$ free -h
               total        used        free      shared  buff/cache   available
Mem:           3.4Gi       674Mi       798Mi       2.0Mi       2.2Gi       2.7Gi
Swap:          3.4Gi       137Mi       3.2Gi

$ sudo dnf install --enablerepo="*" -y sysbench

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=60 --report-interval=1 prepare
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Creating table 'sbtest1'...
Inserting 1000000 records into 'sbtest1'
Creating a secondary index on 'sbtest1'...
Creating table 'sbtest2'...
Inserting 1000000 records into 'sbtest2'
Creating a secondary index on 'sbtest2'...
Creating table 'sbtest3'...
Inserting 1000000 records into 'sbtest3'
Creating a secondary index on 'sbtest3'...
Creating table 'sbtest4'...
Inserting 1000000 records into 'sbtest4'
Creating a secondary index on 'sbtest4'...
Creating table 'sbtest5'...
Inserting 1000000 records into 'sbtest5'
Creating a secondary index on 'sbtest5'...
Creating table 'sbtest6'...
Inserting 1000000 records into 'sbtest6'
Creating a secondary index on 'sbtest6'...
Creating table 'sbtest7'...
Inserting 1000000 records into 'sbtest7'
Creating a secondary index on 'sbtest7'...
Creating table 'sbtest8'...
Inserting 1000000 records into 'sbtest8'
Creating a secondary index on 'sbtest8'...
Creating table 'sbtest9'...
Inserting 1000000 records into 'sbtest9'
Creating a secondary index on 'sbtest9'...
Creating table 'sbtest10'...
Inserting 1000000 records into 'sbtest10'
Creating a secondary index on 'sbtest10'...

1スレッド9.6

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 run
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 1 tps: 127.89 qps: 2572.72 (r/w/o: 1804.40/511.55/256.77) lat (ms,95%): 9.22 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 1 tps: 133.06 qps: 2664.15 (r/w/o: 1862.81/535.23/266.12) lat (ms,95%): 8.74 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 1 tps: 123.00 qps: 2458.00 (r/w/o: 1722.00/490.00/246.00) lat (ms,95%): 17.63 err/s: 0.00 reconn/s: 0.00
[ 4s ] thds: 1 tps: 128.00 qps: 2562.96 (r/w/o: 1791.97/514.99/256.00) lat (ms,95%): 11.87 err/s: 0.00 reconn/s: 0.00
[ 5s ] thds: 1 tps: 137.00 qps: 2736.04 (r/w/o: 1918.03/544.01/274.00) lat (ms,95%): 8.58 err/s: 0.00 reconn/s: 0.00
[ 6s ] thds: 1 tps: 142.00 qps: 2840.00 (r/w/o: 1988.00/568.00/284.00) lat (ms,95%): 8.43 err/s: 0.00 reconn/s: 0.00
[ 7s ] thds: 1 tps: 144.00 qps: 2880.03 (r/w/o: 2016.02/576.01/288.00) lat (ms,95%): 8.43 err/s: 0.00 reconn/s: 0.00
[ 8s ] thds: 1 tps: 150.00 qps: 2990.96 (r/w/o: 2090.97/599.99/300.00) lat (ms,95%): 8.74 err/s: 0.00 reconn/s: 0.00
[ 9s ] thds: 1 tps: 150.00 qps: 3013.02 (r/w/o: 2109.01/604.00/300.00) lat (ms,95%): 8.28 err/s: 0.00 reconn/s: 0.00
[ 10s ] thds: 1 tps: 153.00 qps: 3058.97 (r/w/o: 2141.98/610.99/306.00) lat (ms,95%): 8.13 err/s: 0.00 reconn/s: 0.00

..
[ 298s ] thds: 1 tps: 199.00 qps: 3976.96 (r/w/o: 2782.97/795.99/398.00) lat (ms,95%): 5.47 err/s: 0.00 reconn/s: 0.00
[ 299s ] thds: 1 tps: 197.00 qps: 3933.99 (r/w/o: 2751.99/788.00/394.00) lat (ms,95%): 5.77 err/s: 0.00 reconn/s: 0.00
[ 300s ] thds: 1 tps: 199.00 qps: 3991.97 (r/w/o: 2797.98/795.99/398.00) lat (ms,95%): 5.57 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            784952
        write:                           224272
        other:                           112136
        total:                           1121360
    transactions:                        56068  (186.89 per sec.)
    queries:                             1121360 (3737.81 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          300.0038s
    total number of events:              56068

Latency (ms):
         min:                                    4.42
         avg:                                    5.35
         max:                                   36.29
         95th percentile:                        6.79
         sum:                               299950.61

Threads fairness:
    events (avg/stddev):           56068.0000/0.00
    execution time (avg/stddev):   299.9506/0.00

起動しているmysqldを公式ビルドの9.6から9.7に取り換え。

$ pkill mysqld

$ bin/mysqld --no-defaults --initialize-insecure --lower_case_table_names=1
$ bin/mysqld --no-defaults --lower_case_table_names=1 --innodb-dedicated-server=1 --log-error-verbosity=3 --innodb-monitor-enable=all --daemonize

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=60 --report-interval=1 prepare

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 run
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 1 tps: 136.87 qps: 2752.47 (r/w/o: 1930.22/547.50/274.75) lat (ms,95%): 8.13 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 1 tps: 129.06 qps: 2581.26 (r/w/o: 1806.88/516.25/258.13) lat (ms,95%): 12.98 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 1 tps: 126.00 qps: 2523.99 (r/w/o: 1764.00/508.00/252.00) lat (ms,95%): 20.37 err/s: 0.00 reconn/s: 0.00
[ 4s ] thds: 1 tps: 139.00 qps: 2775.96 (r/w/o: 1945.97/551.99/278.00) lat (ms,95%): 8.74 err/s: 0.00 reconn/s: 0.00
[ 5s ] thds: 1 tps: 148.00 qps: 2964.02 (r/w/o: 2072.01/596.00/296.00) lat (ms,95%): 8.13 err/s: 0.00 reconn/s: 0.00
[ 6s ] thds: 1 tps: 154.00 qps: 3076.03 (r/w/o: 2156.02/612.01/308.00) lat (ms,95%): 7.98 err/s: 0.00 reconn/s: 0.00
[ 7s ] thds: 1 tps: 159.00 qps: 3180.02 (r/w/o: 2226.01/636.00/318.00) lat (ms,95%): 7.84 err/s: 0.00 reconn/s: 0.00
[ 8s ] thds: 1 tps: 156.00 qps: 3123.00 (r/w/o: 2184.00/627.00/312.00) lat (ms,95%): 7.70 err/s: 0.00 reconn/s: 0.00
[ 9s ] thds: 1 tps: 161.00 qps: 3220.98 (r/w/o: 2253.98/645.00/322.00) lat (ms,95%): 8.13 err/s: 0.00 reconn/s: 0.00
[ 10s ] thds: 1 tps: 163.00 qps: 3256.00 (r/w/o: 2282.00/648.00/326.00) lat (ms,95%): 8.28 err/s: 0.00 reconn/s: 0.00

..
[ 295s ] thds: 1 tps: 214.00 qps: 4280.01 (r/w/o: 2996.01/856.00/428.00) lat (ms,95%): 5.09 err/s: 0.00 reconn/s: 0.00
[ 296s ] thds: 1 tps: 212.99 qps: 4259.88 (r/w/o: 2981.92/851.98/425.99) lat (ms,95%): 5.37 err/s: 0.00 reconn/s: 0.00
[ 297s ] thds: 1 tps: 213.01 qps: 4260.10 (r/w/o: 2982.07/852.02/426.01) lat (ms,95%): 5.28 err/s: 0.00 reconn/s: 0.00
[ 298s ] thds: 1 tps: 212.00 qps: 4239.96 (r/w/o: 2967.97/847.99/424.00) lat (ms,95%): 5.37 err/s: 0.00 reconn/s: 0.00
[ 299s ] thds: 1 tps: 215.00 qps: 4300.02 (r/w/o: 3010.01/860.00/430.00) lat (ms,95%): 5.18 err/s: 0.00 reconn/s: 0.00
[ 300s ] thds: 1 tps: 210.00 qps: 4200.05 (r/w/o: 2940.04/840.01/420.01) lat (ms,95%): 5.47 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            848134
        write:                           242324
        other:                           121162
        total:                           1211620
    transactions:                        60581  (201.93 per sec.)
    queries:                             1211620 (4038.70 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          300.0021s
    total number of events:              60581

Latency (ms):
         min:                                    3.99
         avg:                                    4.95
         max:                                   41.05
         95th percentile:                        6.55
         sum:                               299950.65

Threads fairness:
    events (avg/stddev):           60581.0000/0.00
    execution time (avg/stddev):   299.9506/0.00

おおちょっと良さそう。抜粋。

9.6.09.7.0-er
min4.423.99
avg5.3554.95
max36.2941.05
95%ile6.796.55
QPS3737.814038.70

95%ileで3%くらいレイテンシーが減ってる。sysbenchでプロファイルして当ててる可能性はあるのでsysbenchでのスコアが余計に良く出る可能性はあるので、あとで tpcc-mysql でも当ててみたい(が、1行引いて云々…なワークロードだったら全体的にレイテンシ下がるかな?)

スレッド数8(MySQL側のスレッド数を超えさせる)でもやってみる。daemon起動し直すのが面倒な関係で9.7.0-erが先。

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 cleanup
$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 prepare

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 --threads=8 run
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 8
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 8 tps: 557.63 qps: 11257.47 (r/w/o: 7892.72/2241.50/1123.25) lat (ms,95%): 31.94 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 8 tps: 728.16 qps: 14585.28 (r/w/o: 10213.30/2915.66/1456.33) lat (ms,95%): 16.41 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 8 tps: 816.00 qps: 16307.02 (r/w/o: 11412.01/3263.00/1632.00) lat (ms,95%): 15.55 err/s: 0.00 reconn/s: 0.00
[ 4s ] thds: 8 tps: 817.99 qps: 16397.83 (r/w/o: 11470.88/3290.97/1635.98) lat (ms,95%): 16.12 err/s: 0.00 reconn/s: 0.00
[ 5s ] thds: 8 tps: 717.99 qps: 14305.89 (r/w/o: 10021.92/2847.98/1435.99) lat (ms,95%): 20.37 err/s: 0.00 reconn/s: 0.00
[ 6s ] thds: 8 tps: 869.01 qps: 17422.16 (r/w/o: 12192.11/3492.03/1738.02) lat (ms,95%): 15.27 err/s: 0.00 reconn/s: 0.00
[ 7s ] thds: 8 tps: 914.97 qps: 18271.37 (r/w/o: 12790.56/3650.87/1829.94) lat (ms,95%): 13.95 err/s: 0.00 reconn/s: 0.00
[ 8s ] thds: 8 tps: 917.03 qps: 18337.55 (r/w/o: 12835.38/3668.11/1834.05) lat (ms,95%): 13.70 err/s: 0.00 reconn/s: 0.00
[ 9s ] thds: 8 tps: 925.00 qps: 18490.97 (r/w/o: 12939.98/3700.99/1850.00) lat (ms,95%): 13.70 err/s: 0.00 reconn/s: 0.00
[ 10s ] thds: 8 tps: 921.00 qps: 18443.03 (r/w/o: 12915.02/3686.01/1842.00) lat (ms,95%): 14.46 err/s: 0.00 reconn/s: 0.00

..
[ 295s ] thds: 8 tps: 1018.00 qps: 20369.91 (r/w/o: 14260.94/4071.98/2036.99) lat (ms,95%): 10.65 err/s: 0.00 reconn/s: 0.00
[ 296s ] thds: 8 tps: 1004.99 qps: 20091.88 (r/w/o: 14064.91/4016.98/2009.99) lat (ms,95%): 11.45 err/s: 0.00 reconn/s: 0.00
[ 297s ] thds: 8 tps: 987.99 qps: 19727.73 (r/w/o: 13806.81/3946.95/1973.97) lat (ms,95%): 11.45 err/s: 0.00 reconn/s: 0.00
[ 298s ] thds: 8 tps: 1001.00 qps: 20052.10 (r/w/o: 14036.07/4012.02/2004.01) lat (ms,95%): 11.24 err/s: 0.00 reconn/s: 0.00
[ 299s ] thds: 8 tps: 1010.01 qps: 20216.15 (r/w/o: 14156.10/4040.03/2020.01) lat (ms,95%): 11.45 err/s: 0.00 reconn/s: 0.00
[ 300s ] thds: 8 tps: 1006.00 qps: 20080.93 (r/w/o: 14049.95/4018.99/2011.99) lat (ms,95%): 11.04 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            4088658
        write:                           1168188
        other:                           584094
        total:                           5840940
    transactions:                        292047 (973.46 per sec.)
    queries:                             5840940 (19469.20 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          300.0089s
    total number of events:              292047

Latency (ms):
         min:                                    4.66
         avg:                                    8.22
         max:                                  117.24
         95th percentile:                       12.08
         sum:                              2399733.89

Threads fairness:
    events (avg/stddev):           36505.8750/71.89
    execution time (avg/stddev):   299.9667/0.00

同じく9.6.0にバイナリを取り換えて。

$ pkill mysqld

$ cd ../mysql-9.6.0-linux-glibc2.28-x86_64
$ bin/mysqld --no-defaults --lower_case_table_names=1 --innodb-dedicated-server=1 --log-error-verbosity=3 --innodb-monitor-enable=all --daemonize

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 --threads=8 cleanup
$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 --threads=8 prepare

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 --threads=8 run
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 8
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 8 tps: 534.59 qps: 10793.69 (r/w/o: 7567.17/2149.34/1077.17) lat (ms,95%): 26.68 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 8 tps: 609.20 qps: 12222.01 (r/w/o: 8555.81/2447.80/1218.40) lat (ms,95%): 16.71 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 8 tps: 700.99 qps: 13986.84 (r/w/o: 9786.89/2797.97/1401.98) lat (ms,95%): 17.95 err/s: 0.00 reconn/s: 0.00
[ 4s ] thds: 8 tps: 724.98 qps: 14506.62 (r/w/o: 10163.73/2892.92/1449.96) lat (ms,95%): 18.28 err/s: 0.00 reconn/s: 0.00
[ 5s ] thds: 8 tps: 677.02 qps: 13543.40 (r/w/o: 9476.28/2713.08/1354.04) lat (ms,95%): 18.28 err/s: 0.00 reconn/s: 0.00
[ 6s ] thds: 8 tps: 755.98 qps: 15115.67 (r/w/o: 10584.77/3018.93/1511.97) lat (ms,95%): 17.95 err/s: 0.00 reconn/s: 0.00
[ 7s ] thds: 8 tps: 791.99 qps: 15830.89 (r/w/o: 11075.92/3170.98/1583.99) lat (ms,95%): 15.27 err/s: 0.00 reconn/s: 0.00
[ 8s ] thds: 8 tps: 802.01 qps: 16039.30 (r/w/o: 11231.21/3204.06/1604.03) lat (ms,95%): 15.55 err/s: 0.00 reconn/s: 0.00
[ 9s ] thds: 8 tps: 809.01 qps: 16229.17 (r/w/o: 11351.12/3260.04/1618.02) lat (ms,95%): 15.27 err/s: 0.00 reconn/s: 0.00
[ 10s ] thds: 8 tps: 821.01 qps: 16401.14 (r/w/o: 11487.10/3272.03/1642.01) lat (ms,95%): 15.83 err/s: 0.00 reconn/s: 0.00

..
[ 295s ] thds: 8 tps: 883.02 qps: 17675.43 (r/w/o: 12373.30/3534.09/1768.04) lat (ms,95%): 13.22 err/s: 0.00 reconn/s: 0.00
[ 296s ] thds: 8 tps: 884.00 qps: 17699.00 (r/w/o: 12389.00/3541.00/1769.00) lat (ms,95%): 12.52 err/s: 0.00 reconn/s: 0.00
[ 297s ] thds: 8 tps: 873.99 qps: 17499.79 (r/w/o: 12258.85/3492.96/1747.98) lat (ms,95%): 13.22 err/s: 0.00 reconn/s: 0.00
[ 298s ] thds: 8 tps: 883.99 qps: 17660.87 (r/w/o: 12351.91/3540.97/1767.99) lat (ms,95%): 12.52 err/s: 0.00 reconn/s: 0.00
[ 299s ] thds: 8 tps: 894.99 qps: 17912.73 (r/w/o: 12538.81/3584.95/1788.97) lat (ms,95%): 12.52 err/s: 0.00 reconn/s: 0.00
[ 300s ] thds: 8 tps: 794.01 qps: 15863.25 (r/w/o: 11110.18/3164.05/1589.03) lat (ms,95%): 13.46 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            3589908
        write:                           1025688
        other:                           512844
        total:                           5128440
    transactions:                        256422 (854.72 per sec.)
    queries:                             5128440 (17094.31 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          300.0082s
    total number of events:              256422

Latency (ms):
         min:                                    5.09
         avg:                                    9.36
         max:                                  132.09
         95th percentile:                       13.95
         sum:                              2399787.50

Threads fairness:
    events (avg/stddev):           32052.7500/66.58
    execution time (avg/stddev):   299.9734/0.00

スレッド数が上がっても同傾向だった。

9.6.09.7.0-er
min5.094.66
avg9.368.22
max132.09117.24
95%ile13.9512.08
QPS17094.3119469.20

パッと見良さそう。

他には

  • 今はバッファプールに全てデータが収まっているはずなので、収まりきらないサイズでの比較(これはI/Oが支配的になるのでどっちでも大して変わらなくなるんじゃないかなあ)
  • sysbench oltp*以外のベンチマークスイート(1行引いて1行更新系のやつだったら同じ傾向を示すような気がする、OLAP系はもともと完走できる気がしないしどうしようかな…)
  • もうちょっと真面目にチューニングして比較
  • ビルドオプション揃えてPGOが効いてないだけのソースビルドとPGOが効いてるビルド済みバイナリの比較
    あたりが思いつくので誰か試してください。

今度こそ自力ビルドのMySQL 9.7.0-erと公式ビルドMySQL 9.7.0-erの測り比べ

$
0
0

前回とは趣向を変えて、自力ビルドと公式ビルドの差を試す。

自力ビルドは大したオプションを与えずにコンパイルするだけ。

$ cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql ../mysql-9.7.0-er
$ make -j4
$ sudo make install
$ cd /usr/local/mysql

$ bin/mysqld --no-defaults --initialize-insecure --lower_case_table_names=1
$ bin/mysqld --no-defaults --lower_case_table_names=1 --innodb-dedicated-server=1 --log-error-verbosity=3 --innodb-monitor-enable=all --daemonize

前回と同じoltp_read_writeで比較。

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=60 --report-interval=1 --threads=8 prepare

$ sysbench --mysql-host=10.0.0.177 --mysql-user=sbtest oltp_read_write --table_size=1000000 --tables=10 --time=300 --report-interval=1 run
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 1
Report intermediate results every 1 second(s)
Initializing random number generator from current time

Initializing worker threads...

Threads started!

[ 1s ] thds: 1 tps: 91.91 qps: 1855.27 (r/w/o: 1300.79/369.66/184.83) lat (ms,95%): 12.52 err/s: 0.00 reconn/s: 0.00
[ 2s ] thds: 1 tps: 95.05 qps: 1898.92 (r/w/o: 1330.65/378.18/190.09) lat (ms,95%): 12.30 err/s: 0.00 reconn/s: 0.00
[ 3s ] thds: 1 tps: 92.00 qps: 1844.01 (r/w/o: 1288.01/372.00/184.00) lat (ms,95%): 12.52 err/s: 0.00 reconn/s: 0.00
[ 4s ] thds: 1 tps: 85.00 qps: 1698.98 (r/w/o: 1189.98/339.00/170.00) lat (ms,95%): 25.74 err/s: 0.00 reconn/s: 0.00
[ 5s ] thds: 1 tps: 89.00 qps: 1777.02 (r/w/o: 1246.02/353.00/178.00) lat (ms,95%): 25.28 err/s: 0.00 reconn/s: 0.00
[ 6s ] thds: 1 tps: 98.00 qps: 1959.99 (r/w/o: 1372.00/392.00/196.00) lat (ms,95%): 11.87 err/s: 0.00 reconn/s: 0.00
[ 7s ] thds: 1 tps: 97.00 qps: 1937.00 (r/w/o: 1355.00/388.00/194.00) lat (ms,95%): 11.87 err/s: 0.00 reconn/s: 0.00
[ 8s ] thds: 1 tps: 97.00 qps: 1947.02 (r/w/o: 1361.01/392.00/194.00) lat (ms,95%): 11.65 err/s: 0.00 reconn/s: 0.00
[ 9s ] thds: 1 tps: 97.00 qps: 1936.00 (r/w/o: 1358.00/384.00/194.00) lat (ms,95%): 11.87 err/s: 0.00 reconn/s: 0.00
[ 10s ] thds: 1 tps: 98.00 qps: 1962.99 (r/w/o: 1371.99/395.00/196.00) lat (ms,95%): 11.87 err/s: 0.00 reconn/s: 0.00

..
[ 295s ] thds: 1 tps: 110.00 qps: 2196.00 (r/w/o: 1536.00/440.00/220.00) lat (ms,95%): 9.73 err/s: 0.00 reconn/s: 0.00
[ 296s ] thds: 1 tps: 107.00 qps: 2158.01 (r/w/o: 1512.01/432.00/214.00) lat (ms,95%): 9.91 err/s: 0.00 reconn/s: 0.00
[ 297s ] thds: 1 tps: 109.00 qps: 2179.98 (r/w/o: 1525.99/436.00/218.00) lat (ms,95%): 9.73 err/s: 0.00 reconn/s: 0.00
[ 298s ] thds: 1 tps: 108.00 qps: 2159.99 (r/w/o: 1511.99/432.00/216.00) lat (ms,95%): 9.91 err/s: 0.00 reconn/s: 0.00
[ 299s ] thds: 1 tps: 108.00 qps: 2155.02 (r/w/o: 1511.01/428.00/216.00) lat (ms,95%): 9.73 err/s: 0.00 reconn/s: 0.00
[ 300s ] thds: 1 tps: 107.00 qps: 2145.00 (r/w/o: 1499.00/432.00/214.00) lat (ms,95%): 9.73 err/s: 0.00 reconn/s: 0.00
SQL statistics:
    queries performed:
        read:                            448252
        write:                           128072
        other:                           64036
        total:                           640360
    transactions:                        32018  (106.73 per sec.)
    queries:                             640360 (2134.52 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          300.0011s
    total number of events:              32018

Latency (ms):
         min:                                    8.39
         avg:                                    9.37
         max:                                   68.71
         95th percentile:                       10.46
         sum:                               299966.56

Threads fairness:
    events (avg/stddev):           32018.0000/0.00
    execution time (avg/stddev):   299.9666/0.00

前回とはベンチマーククライアントマシンが(論理スレッド数とメモリサイズは同じはずなのに)変わっていて結果が盛大に乖離したので、9.7公式ビルド版も測り直してまとめ。ついでに9.6.0の自前ビルドも測った。

9.6.0(公式バイナリ)9.7.0-er(公式バイナリ)9.7.0-er(自力ビルド)9.6.0(自力ビルド)
min7.657.418.398.00
avg8.468.299.378.91
max91.4546.7368.71346.42
95%ile9.7310.0910.4610.46
QPS2363.802412.582134.522244.01

5.5の頃に測った感じの「公式バイナリの方が自力ビルドよりも数%レイテンシが良い」(gccの最新版とかiccとか使ってようやくトントンくらいまで行くくらい)は今も健在で、それがPGOによって数%から10%以上に差が開いた感じかしらん。

なお自前ビルドでもPGOを有効にしたいなら @i_rethiさんの記事がやり方を解説してくれている(ので、自分ではやらないつもり)

MySQL 8.0.36でPGOを使ってビルド、性能比較してみる - hiroi10のブログ

MySQL 5.7とそれ以前で変な作り方をしたトリガーがmysqldumpで流し込んだ時にSyntax Errorでエラーになる

$
0
0

そもそもこの構文破壊されたCREATE TRIGGER文は、DELIMITERの変更とバージョン指定コメント構文が揃わないと起こらないので、おそらくmysqldumpで引っこ抜いたトリガー定義を間違っていじったりコピペし間違えた時くらいしか起こらない気がする。

が、一度この状態になるとそれ以降のmysqldumpで発火する。

How to repeat

  • MySQL 5.7とそれ以前に対して以下を実行すると、CREATE TRIGGERは成功するけどSyntax Errorが返ってくる。
CREATE DATABASE d1;
CREATE TABLE d1.t1 (num INT);

DELIMITER ;;
/*!50003 CREATE TRIGGER d1.test BEFORE INSERT ON d1.t1 FOR EACH ROW INSERT INTO d1.t1 VALUES (1); */;;
DELIMITER ;
  • こんな風に。
mysql57 5> CREATE DATABASE d1;
Query OK, 1 row affected (0.00 sec)

mysql57 5> CREATE TABLE d1.t1 (num INT);
Query OK, 0 rows affected (0.01 sec)

mysql57 5> DELIMITER ;;

mysql57 5> /*!50003 CREATE TRIGGER d1.test BEFORE INSERT ON d1.t1 FOR EACH ROW INSERT INTO d1.t1 VALUES (1); */;;
Query OK, 0 rows affected (0.00 sec)

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '*/' at line 1

mysql57 5> DELIMITER ;

バージョン指定コメント構文をパースする時にDELIMITERの指定が効いてなくて、コメントの中の ;でクエリが終端したと思って実行、その後残った */;;がDELIMITERが効いた状態で解釈されてシンタックスエラーになってるんじゃないかと思う。

ジェネラルログで見ると割と微妙で、コメントの開きは記録されてるけど閉じが記録されてない(5.7の時代ではデフォルトFALSEだった —commentsはちゃんと有効にしてある)

2026-04-02T06:07:29.232360-00:00            6 Connect   root@localhost on  using Socket
2026-04-02T06:07:29.232486-00:00            6 Query     select @@version_comment limit 1
2026-04-02T06:07:31.155143-00:00            6 Query     DROP DATABASE d1
2026-04-02T06:07:31.158391-00:00            6 Query     SELECT DATABASE()
2026-04-02T06:07:33.678277-00:00            6 Query     CREATE DATABASE d1
2026-04-02T06:07:33.678793-00:00            6 Query     CREATE TABLE d1.t1 (num INT)
2026-04-02T06:07:33.686280-00:00            6 Query     /*!50003 CREATE TRIGGER d1.test BEFORE INSERT ON d1.t1 FOR EACH ROW INSERT INTO d1.t1 VALUES (1);

で、この状態で作ったデータベースをmysqldumpすると(ワーニングはこの件に直接関係ないので無視していい)

$ mysqldump57 d1 --triggers > d1.sql
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don't want to restore GTIDs, pass --set-gtid-purged=OFF. To make a complete dump, pass --all-databases --triggers --routines --events.
Warning: A dump from a server that has GTIDs enabled will by default include the GTIDs of all transactions, even those that were executed during its extraction and might not be represented in the dumped data. This might result in an inconsistent data dump.
In order to ensure a consistent backup of the database, pass --single-transaction or --lock-all-tables or --master-data.

↓のようにまたこれが再生産されるので

$ grep TRIGGER d1.sql
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER d1.test BEFORE INSERT ON d1.t1 FOR EACH ROW INSERT INTO d1.t1 VALUES (1); */;;

このダンプを食わせようとするとエラって止まる。

mysql57 11> DROP DATABASE d1;
Query OK, 1 row affected (0.00 sec)

mysql57 13> CREATE DATABASE d1;
Query OK, 1 row affected (0.00 sec)

$ mysql57 -v d1 < d1.sql
--------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */
--------------
..

--------------
/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER d1.test BEFORE INSERT ON d1.t1 FOR EACH ROW INSERT INTO d1.t1 VALUES (1); */
--------------

ERROR 1064 (42000) at line 49: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '*/' at line 1

そのくせちゃんとトリガーはできているという曲者。。

mysql57 15> SHOW TRIGGERS\G
*************************** 1. row ***************************
             Trigger: test
               Event: INSERT
               Table: t1
           Statement: INSERT INTO d1.t1 VALUES (1);
              Timing: BEFORE
             Created: 2026-04-02 06:14:44.43
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
             Definer: root@localhost
character_set_client: latin1
collation_connection: latin1_swedish_ci
  Database Collation: utf8mb4_general_ci
1 row in set (0.00 sec)

mysql57 15> SHOW CREATE TRIGGER d1.test\G
*************************** 1. row ***************************
               Trigger: test
              sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
SQL Original Statement: CREATE DEFINER=`root`@`localhost` TRIGGER d1.test BEFORE INSERT ON d1.t1 FOR EACH ROW INSERT INTO d1.t1 VALUES (1);
  character_set_client: latin1
  collation_connection: latin1_swedish_ci
    Database Collation: utf8mb4_general_ci
               Created: 2026-04-02 06:14:44.43
1 row in set (0.00 sec)

この時点で SQL Original Statement にセミコロンが含まれてしまっている。
シングルステートメントで正しく作った場合はセミコロンは含まれない。

mysql57 15> CREATE TRIGGER d1.test2 BEFORE UPDATE ON d1.t1 FOR EACH ROW DELETE FROM d1.t1 WHERE num = NEW.num;
Query OK, 0 rows affected (0.11 sec)

mysql57 15> DELIMITER ;;

mysql57 15> CREATE TRIGGER d1.test3 BEFORE UPDATE ON d1.t1 FOR EACH ROW DELETE FROM d1.t1 WHERE num = NEW.num;;
Query OK, 0 rows affected (0.00 sec)

mysql57 15> DELIMITER ;

mysql57 15> SHOW TRIGGERS\G
*************************** 1. row ***************************
             Trigger: test
               Event: INSERT
               Table: t1
           Statement: INSERT INTO d1.t1 VALUES (1);
              Timing: BEFORE
             Created: 2026-04-02 06:14:44.43
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
             Definer: root@localhost
character_set_client: latin1
collation_connection: latin1_swedish_ci
  Database Collation: utf8mb4_general_ci
*************************** 2. row ***************************
             Trigger: test2
               Event: UPDATE
               Table: t1
           Statement: DELETE FROM d1.t1 WHERE num = NEW.num
              Timing: BEFORE
             Created: 2026-04-02 06:16:52.01
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
             Definer: root@localhost
character_set_client: latin1
collation_connection: latin1_swedish_ci
  Database Collation: utf8mb4_general_ci
*************************** 3. row ***************************
             Trigger: test3
               Event: UPDATE
               Table: t1
           Statement: DELETE FROM d1.t1 WHERE num = NEW.num
              Timing: BEFORE
             Created: 2026-04-02 06:17:29.12
            sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
             Definer: root@localhost
character_set_client: latin1
collation_connection: latin1_swedish_ci
  Database Collation: utf8mb4_general_ci
3 rows in set (0.00 sec)

そもそもこの ; */;;が現れるのがmysqldump由来だろうから最初に間違わなければいいんだけれど、当たってしまったので(そして5.7はもうEOLでバグレポートしても相手にしてもらえないので)供養のために記録しておく。

エラるのが確実にこのCREATE TRIGGER由来だと断言できるなら mysql -fで無視させるもよし、多少自由がが効くなら取ったmysqldumpファイルをエディタで編集して ; */;;;の方を削ってやるもよし。


ちなみに8.0とそれ以降だと、綺麗にシンタックスエラーになってトリガーがそもそも出来上がらないのでリストアできないmysqldumpファイルも出来上がらない。


【202/04/02 16:09】

対象のトリガーがあるかどうかを調べるSQLはたぶんこれでいいはず(「ステートメントがセミコロンで終わっているもの」、実際見つかった)

SELECT trigger_schema, trigger_name, action_statement FROM information_schema.triggers WHERE action_statement LIKE '%;';

対象の​

たt

InnoDB FTSがメモリを食う件その2 tcmallocでいくらかマシになりそう

$
0
0

日々の覚書: InnoDB FULLTEXT KEYがメモリを食う件(未解決) の続き。

何がメモリを使ってるのかを performance_schema.memory_summary_global_by_event_nameテーブルで(できないだろうなと思いながら)観測してみる。

mysql80> RESTART;
$ sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_read_write --table_size=1000000 --time=600 --report-interval=1 cleanup
$ sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_read_write --table_size=1000000 --time=600 --report-interval=1 prepare
$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date

$ ps -Ao pid,fname,rss | grep 3591371
3591371 mysqld   498060

↑まずはフルテキストインデックスなしの状態でベンチを流した後の memory_summary_global_by_event_name

mysql80 74> SELECT *, format_bytes(SUM(current_number_of_bytes_used) OVER (ORDER BY current_number_of_bytes_used DESC)) FROM performance_schema.memory_summary_global_by_event_name WHERE event_name NOT LIKE 'memory/performance_schema/%' ORDER BY current_number_of_bytes_used DESC LIMIT 10;
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
| EVENT_NAME                      | COUNT_ALLOC | COUNT_FREE | SUM_NUMBER_OF_BYTES_ALLOC | SUM_NUMBER_OF_BYTES_FREE | LOW_COUNT_USED | CURRENT_COUNT_USED | HIGH_COUNT_USED | LOW_NUMBER_OF_BYTES_USED | CURRENT_NUMBER_OF_BYTES_USED | HIGH_NUMBER_OF_BYTES_USED | format_bytes(SUM(current_number_of_bytes_used) OVER (ORDER BY current_number_of_bytes_used DESC)) |
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
| memory/innodb/buf_buf_pool      |           1 |          0 |                  68620288 |                        0 |              0 |                  1 |               1 |                        0 |                     68620288 |                  68620288 | 65.44 MiB                                                                                         |
| memory/innodb/ut0link_buf       |           2 |          0 |                  25165888 |                        0 |              0 |                  2 |               2 |                        0 |                     25165888 |                  25165888 | 89.44 MiB                                                                                         |
| memory/innodb/log_buffer_memory |           1 |          0 |                  16778224 |                        0 |              0 |                  1 |               1 |                        0 |                     16778224 |                  16778224 | 105.44 MiB                                                                                        |
| memory/mysys/KEY_CACHE          |           3 |          0 |                   8390864 |                        0 |              0 |                  3 |               3 |                        0 |                      8390864 |                   8390864 | 113.44 MiB                                                                                        |
| memory/sql/TABLE                |        6028 |       3948 |                  30576081 |                 22566230 |              0 |               2080 |            2082 |                        0 |                      8009851 |                   8041086 | 121.08 MiB                                                                                        |
| memory/innodb/sync0arr          |           3 |          0 |                   7373032 |                        0 |              0 |                  3 |               3 |                        0 |                      7373032 |                   7373032 | 128.11 MiB                                                                                        |
| memory/sql/log_sink_pfs         |           3 |          2 |                   5292128 |                    49216 |              0 |                  1 |               2 |                        0 |                      5242912 |                   5259328 | 133.11 MiB                                                                                        |
| memory/innodb/lock0lock         |          33 |          0 |                   5086600 |                        0 |              0 |                 33 |              33 |                        0 |                      5086600 |                   5086600 | 137.97 MiB                                                                                        |
| memory/innodb/ut0pool           |           2 |          0 |                   4194488 |                        0 |              0 |                  2 |               2 |                        0 |                      4194488 |                   4194488 | 141.97 MiB                                                                                        |
| memory/temptable/physical_ram   |          73 |         69 |                  76548384 |                 72353952 |              0 |                  4 |               4 |                        0 |                      4194432 |                   4194432 | 145.97 MiB                                                                                        |
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
10 rows in set (0.00 sec)

mysql80 74> SELECT SUM(current_number_of_bytes_used) FROM performance_schema.memory_summary_global_by_event_name;
+-----------------------------------+
| SUM(current_number_of_bytes_used) |
+-----------------------------------+
|                         410046116 |
+-----------------------------------+
1 row in set (0.00 sec)

mysql80 74> SHOW ENGINE PERFORMANCE_SCHEMA STATUS;
..
| performance_schema | performance_schema.memory                                   | 242396808 |
+--------------------+-------------------------------------------------------------+-----------+
248 rows in set (0.00 sec)

トータルではまあまあ合ってそうな気がする( memory/temptable/physical_ramはたぶん監視に使ってる pmm-agent だな…失敗したかも )

次にALTER TABLEでFULLTEXT INDEXを足す。
この時点でメモリ使用量が増えている(これ自体は不思議ではない)けど、p_sには計上されず…(これがぐぬぬ)

mysql80 90> ALTER TABLE sbtest.sbtest1 ADD FULLTEXT KEY (c);
Query OK, 0 rows affected, 1 warning (1 min 38.24 sec)
Records: 0  Duplicates: 0  Warnings: 1

$ ps -Ao pid,fname,rss | grep 3591371
3591371 mysqld   659868

mysql80 107> SELECT SUM(current_number_of_bytes_used) FROM performance_schema.memory_summary_global_by_event_name;
+-----------------------------------+
| SUM(current_number_of_bytes_used) |
+-----------------------------------+
|                         408419366 |
+-----------------------------------+
1 row in set (0.00 sec)

mysql80 107> SELECT *, format_bytes(SUM(current_number_of_bytes_used) OVER (ORDER BY current_number_of_bytes_used DESC)) FROM performance_schema.memory_summary_global_by_event_name WHERE event_name NOT LIKE 'memory/performance_schema/%' ORDER BY current_number_of_bytes_used DESC LIMIT 10;
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
| EVENT_NAME                      | COUNT_ALLOC | COUNT_FREE | SUM_NUMBER_OF_BYTES_ALLOC | SUM_NUMBER_OF_BYTES_FREE | LOW_COUNT_USED | CURRENT_COUNT_USED | HIGH_COUNT_USED | LOW_NUMBER_OF_BYTES_USED | CURRENT_NUMBER_OF_BYTES_USED | HIGH_NUMBER_OF_BYTES_USED | format_bytes(SUM(current_number_of_bytes_used) OVER (ORDER BY current_number_of_bytes_used DESC)) |
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
| memory/innodb/buf_buf_pool      |           1 |          0 |                  68620288 |                        0 |              0 |                  1 |               1 |                        0 |                     68620288 |                  68620288 | 65.44 MiB                                                                                         |
| memory/innodb/ut0link_buf       |           2 |          0 |                  25165888 |                        0 |              0 |                  2 |               2 |                        0 |                     25165888 |                  25165888 | 89.44 MiB                                                                                         |
| memory/innodb/log_buffer_memory |           1 |          0 |                  16778224 |                        0 |              0 |                  1 |               1 |                        0 |                     16778224 |                  16778224 | 105.44 MiB                                                                                        |
| memory/mysys/KEY_CACHE          |           3 |          0 |                   8390864 |                        0 |              0 |                  3 |               3 |                        0 |                      8390864 |                   8390864 | 113.44 MiB                                                                                        |
| memory/sql/TABLE                |        7177 |       5065 |                  37444758 |                 29329243 |              0 |               2112 |            2113 |                        0 |                      8115515 |                   8130336 | 121.18 MiB                                                                                        |
| memory/innodb/sync0arr          |           3 |          0 |                   7373032 |                        0 |              0 |                  3 |               3 |                        0 |                      7373032 |                   7373032 | 128.22 MiB                                                                                        |
| memory/sql/log_sink_pfs         |           3 |          2 |                   5292128 |                    49216 |              0 |                  1 |               2 |                        0 |                      5242912 |                   5259328 | 133.22 MiB                                                                                        |
| memory/innodb/lock0lock         |          33 |          0 |                   5086600 |                        0 |              0 |                 33 |              33 |                        0 |                      5086600 |                   5086600 | 138.07 MiB                                                                                        |
| memory/innodb/ut0pool           |           2 |          0 |                   4194488 |                        0 |              0 |                  2 |               2 |                        0 |                      4194488 |                   4194488 | 142.07 MiB                                                                                        |
| memory/temptable/physical_ram   |          96 |         92 |                 100666368 |                 96471936 |              0 |                  4 |               4 |                        0 |                      4194432 |                   4194432 | 146.07 MiB                                                                                        |
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
10 rows in set (0.01 sec)

一度リスタートして再度600秒ベンチ。

mysql80 107> RESTART;
Query OK, 0 rows affected (0.00 sec)

$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date

$ ps -Ao pid,fname,rss | grep 3592261
3592261 mysqld   567808

mysql80 48> SELECT *, format_bytes(SUM(current_number_of_bytes_used) OVER (ORDER BY current_number_of_bytes_used DESC)) FROM performance_schema.memory_summary_global_by_event_name WHERE event_name NOT LIKE 'memory/performance_schema/%' ORDER BY current_number_of_bytes_used DESC LIMIT 10;
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
| EVENT_NAME                      | COUNT_ALLOC | COUNT_FREE | SUM_NUMBER_OF_BYTES_ALLOC | SUM_NUMBER_OF_BYTES_FREE | LOW_COUNT_USED | CURRENT_COUNT_USED | HIGH_COUNT_USED | LOW_NUMBER_OF_BYTES_USED | CURRENT_NUMBER_OF_BYTES_USED | HIGH_NUMBER_OF_BYTES_USED | format_bytes(SUM(current_number_of_bytes_used) OVER (ORDER BY current_number_of_bytes_used DESC)) |
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
| memory/innodb/buf_buf_pool      |           1 |          0 |                  68620288 |                        0 |              0 |                  1 |               1 |                        0 |                     68620288 |                  68620288 | 65.44 MiB                                                                                         |
| memory/innodb/ut0link_buf       |           2 |          0 |                  25165888 |                        0 |              0 |                  2 |               2 |                        0 |                     25165888 |                  25165888 | 89.44 MiB                                                                                         |
| memory/innodb/log_buffer_memory |           1 |          0 |                  16778224 |                        0 |              0 |                  1 |               1 |                        0 |                     16778224 |                  16778224 | 105.44 MiB                                                                                        |
| memory/mysys/KEY_CACHE          |           3 |          0 |                   8390864 |                        0 |              0 |                  3 |               3 |                        0 |                      8390864 |                   8390864 | 113.44 MiB                                                                                        |
| memory/sql/TABLE                |        4204 |       2239 |                  19897956 |                 12217500 |              0 |               1965 |             895 |                        0 |                      7680456 |                   7680456 | 120.77 MiB                                                                                        |
| memory/innodb/sync0arr          |           3 |          0 |                   7373032 |                        0 |              0 |                  3 |               3 |                        0 |                      7373032 |                   7373032 | 127.80 MiB                                                                                        |
| memory/sql/log_sink_pfs         |           3 |          2 |                   5292128 |                    49216 |              0 |                  1 |               2 |                        0 |                      5242912 |                   5259328 | 132.80 MiB                                                                                        |
| memory/innodb/lock0lock         |          33 |          0 |                   5086600 |                        0 |              0 |                 33 |              33 |                        0 |                      5086600 |                   5086600 | 137.65 MiB                                                                                        |
| memory/innodb/ut0pool           |           2 |          0 |                   4194488 |                        0 |              0 |                  2 |               2 |                        0 |                      4194488 |                   4194488 | 141.65 MiB                                                                                        |
| memory/temptable/physical_ram   |          37 |         33 |                  38798496 |                 34604064 |              0 |                  4 |               4 |                        0 |                      4194432 |                   4194432 | 145.65 MiB                                                                                        |
+---------------------------------+-------------+------------+---------------------------+--------------------------+----------------+--------------------+-----------------+--------------------------+------------------------------+---------------------------+---------------------------------------------------------------------------------------------------+
10 rows in set (0.02 sec)

mysql80 48> SHOW ENGINE PERFORMANCE_SCHEMA STATUS;

..
| performance_schema | performance_schema.memory                                   | 240987688 |
+--------------------+-------------------------------------------------------------+-----------+
248 rows in set (0.00 sec)

やっぱりperformance_schemaから調べるのは難しそう。
取り敢えずtcmalloc使ったら変わるかどうかの切り分けだけやっておく。

$ grep malloc /etc/my.cnf
malloc-lib=/usr/lib64/libtcmalloc.so

$ lsof -p 3639083 | grep mall
mysqld  3639083 yoku0825  mem    REG              252,0      334544  68185695 /usr/lib64/libtcmalloc.so.4.5.3

$ sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_common --table_size=1000000 cleanup

$ sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_common --table_size=1000000 prepare

$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date

$ ps -Ao pid,fname,rss | grep 3639083
3639083 mysqld   518920

フルテキストインデックス無しのケースは大して変わらず(スコアは気持ち上がったけど複数回測ってないので確かではない)

mysql80 165> ALTER TABLE sbtest.sbtest1 ADD FULLTEXT KEY (c);
Query OK, 0 rows affected, 1 warning (1 min 37.04 sec)
Records: 0  Duplicates: 0  Warnings: 1

$ ps -Ao pid,fname,rss | grep 3639083
3639083 mysqld   704996

mysql80 184> RESTART;
Query OK, 0 rows affected (0.00 sec)

$ date ; sysbench --mysql-socket=/usr/mysql/8.0.45/data/mysql.sock --mysql-user=root oltp_write_only --table_size=1000000 --time=600 --report-interval=1 run ; date

$ ps -Ao pid,fname,rss | grep 3640646
3640646 mysqld   588272

おー、だいぶ穏やかになった。tcmallocに変えて改善するということはメモリのフラグメンテーション関連かな…。

ansibleのcommunity.mysql.mysql_userでauth_socketでroot@localhostを作る

$
0
0

未来の自分にメモ。

MySQLには auth_socketプラグインがあるので、root@localhostをパスワードレスにしてOS rootからしかアクセスできなくすることができる(今日日OS rootに直接sshする機会はないと思うのでつまり sudo mysqlでしかアクセスできなくする)
my.cnfのテンプレートファイルか何かの [mysqld]セクションに plugin_loadを追加(もし、既に plugin_loadを使っていてその後ろに書くのなら plugin_load_addを使う)

plugin_load= auth_socket.so

ユーザーをセットアップする時にroot@localhostはpluginを指定して作る。

- name: create root@localhost
  community.mysql.mysql_user:
    host: localhost
    name: root
    state: present
    login_user: root
    login_host: localhost
    login_unix_socket: "/var/lib/mysql/mysql.sock"
    sql_log_bin: false
    plugin: "auth_socket"

それ以降に別のアカウントを作る時は、OS rootでないとroot@localhostを使えなくなっているので become: trueで作る。

- name: create app user
  community.mysql.mysql_user:
    host: "192.168.0.1"
    name: appuser
    password: "{{ appuser_password }}"
    priv: "app_schema.*:INSERT,UPDATE,SELECT,DELETE"
    login_user: root
    login_host: localhost
    login_unix_socket: "/var/lib/mysql/mysql.sock"
    sql_log_bin: false
  become: true

community.mysql.mysql_user module – Adds or removes a user from a MySQL or MariaDB database — Ansible Community Documentation


MySQL 9.7 Early Release(無印)とMySQL 9.7 Early Release2の違い

$
0
0

TL;DR

  • 何かを直した結果テストケースが膨らんでいるのはわかる
  • その一方で enable_cascade_triggersなる機能が増えていて面白かった
  • 以下ピコ太郎風にお願いします(?)

I have a MySQL 9.7 Early Release (first) ..

I have a MySQL 9.7 Early Release 2 ..

Ah!

_人人人人人_
> diff -r <
 ̄Y^Y^Y^Y^Y^ ̄

ということで

$ diff -r mysql-9.7.0-er mysql-9.7.0-er2 | tee diff.txt
..
diff -r mysql-9.7.0-er/sql/sys_vars.cc mysql-9.7.0-er2/sql/sys_vars.cc
7691a7692,7727
>
> /**
>   Warn usage of enable_cascade_triggers variable. When it is set
>   to false, warning should include triggers do not fire during FK cascade.
> */
> bool enable_cascade_triggers_check(sys_var *self, THD *thd, set_var *setv) {
>   if (setv->save_result.ulonglong_value == 0)
>     push_warning_printf(
>         thd, Sql_condition::SL_WARNING, ER_WARN_DEPRECATED_WITH_NOTE,
>         ER_THD(thd, ER_WARN_DEPRECATED_WITH_NOTE), self->name.str,
>         "Triggers on child table will not fire during foreign key cascade.");
>   else {
>     if (!is_sql_fk_checks_enabled(thd)) {
>       push_warning_printf(
>           thd, Sql_condition::SL_WARNING, ER_WARN_DEPRECATED_WITH_NOTE,
>           ER_THD(thd, ER_WARN_DEPRECATED_WITH_NOTE), self->name.str,
>           "Enabling trigger execution on child table is supported only with ">           "SQL Foreign Key handling ">           "(i.e with innodb_native_foreign_keys = OFF).");
>     } else {
>       push_warning_printf(thd, Sql_condition::SL_WARNING,
>                           ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT,
>                           ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT),
>                           self->name.str);
>     }
>   }
>   return false;
> }
>
> Sys_var_bool Sys_enable_cascade_triggers(
>     "enable_cascade_triggers",
>     "Execute trigger on child tables during foreign key cascade operations for ">     "SQL Engine foreign key handling(i.e. innodb_native_foreign_keys = OFF).",
>     SESSION_VAR(enable_cascade_triggers),
>     CMD_LINE(OPT_ARG, OPT_CASCADE_TRIGGERS), DEFAULT(false), NO_MUTEX_GUARD,
>     IN_BINLOG, ON_CHECK(enable_cascade_triggers_check), ON_UPDATE(nullptr));

..

パッと見つかったのは enable_cascade_triggersなるシステム変数が増えているところ。今までのストレージエンジンForeign Keyを「使わなかった時でも」CASCADEされた更新がTRIGGERを叩けるようにする、みたいな意味なんだろうか。

MySQL 8.4でテスト。t1の子にt2をセットしてUPDATEとDELETEをカスケードさせる。

mysql84 8> CREATE TABLE t1 (num serial, val varchar(32));
Query OK, 0 rows affected (0.01 sec)

mysql84 8> CREATE TABLE t2 (num serial, val varchar(32));
Query OK, 0 rows affected (0.01 sec)

mysql84 8> ALTER TABLE t2 ADD FOREIGN KEY (num) REFERENCES t1(num) ON UPDATE CASCADE ON DELETE CASCADE;
Query OK, 0 rows affected (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql84 8> INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three');
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql84 8> INSERT INTO t2 VALUES (1, 'one'), (2, 'two'), (3, 'three');
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

で、カスケードされたt2側にトリガーで記録を残すようにする。

mysql84 8> CREATE TABLE log (num serial, op varchar(32), dt datetime);
Query OK, 0 rows affected (0.01 sec)

mysql84 8> CREATE TRIGGER tr1 AFTER UPDATE ON t2 FOR EACH ROW INSERT INTO log VALUES (OLD.num, 'UPDATE', NOW());
Query OK, 0 rows affected (0.01 sec)

mysql84 8> CREATE TRIGGER tr2 AFTER DELETE ON t2 FOR EACH ROW INSERT INTO log VALUES (OLD.num, 'DELETE', NOW());
Query OK, 0 rows affected (0.00 sec)

で、DELETEしたりUPDATEしたり。

mysql84 8> SELECT * FROM log;
Empty set (0.00 sec)

mysql84 8> DELETE FROM t1 WHERE num = 1;
Query OK, 1 row affected (0.00 sec)

mysql84 8> SELECT * FROM t1;
+-----+-------+
| num | val   |
+-----+-------+
|   2 | two   |
|   3 | three |
+-----+-------+
2 rows in set (0.00 sec)

mysql84 8> SELECT * FROM t2;
+-----+-------+
| num | val   |
+-----+-------+
|   2 | two   |
|   3 | three |
+-----+-------+
2 rows in set (0.00 sec)

mysql84 8> SELECT * FROM log;
Empty set (0.00 sec)

mysql84 8> UPDATE t1 SET num = 102 WHERE val = 'two';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql84 8> SELECT * FROM t1;
+-----+-------+
| num | val   |
+-----+-------+
|   3 | three |
| 102 | two   |
+-----+-------+
2 rows in set (0.00 sec)

mysql84 8> SELECT * FROM t2;
+-----+-------+
| num | val   |
+-----+-------+
|   3 | three |
| 102 | two   |
+-----+-------+
2 rows in set (0.00 sec)

mysql84 8> SELECT * FROM log;
Empty set (0.00 sec)

空っぽ。
これがMySQL 9.7.0-er2で enable_cascade_triggersをONにしてやると

mysql97 15> SELECT @@session.enable_cascade_triggers;
+-----------------------------------+
| @@session.enable_cascade_triggers |
+-----------------------------------+
|                                 0 |
+-----------------------------------+
1 row in set (0.001 sec)

mysql97 15> SET SESSION enable_cascade_triggers = ON;
Query OK, 0 rows affected, 1 warning (0.000 sec)

mysql97 15> SHOW WARNINGS;
+---------+------+----------------------------------------------------------------------------------+
| Level   | Code | Message                                                                          |
+---------+------+----------------------------------------------------------------------------------+
| Warning | 1681 | 'enable_cascade_triggers' is deprecated and will be removed in a future release. |
+---------+------+----------------------------------------------------------------------------------+
1 row in set (0.000 sec)

mysql97 15>
mysql97 15> SELECT @@session.enable_cascade_triggers;
+-----------------------------------+
| @@session.enable_cascade_triggers |
+-----------------------------------+
|                                 1 |
+-----------------------------------+
1 row in set (0.000 sec)

出たー。

mysql97 15> DELETE FROM t1 WHERE num = 1;
Query OK, 1 row affected (0.002 sec)

mysql97 15> UPDATE t1 SET num = 102 WHERE val = 'two';
Query OK, 1 row affected (0.001 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql97 15> SELECT * FROM t1;
+-----+-------+
| num | val   |
+-----+-------+
|   3 | three |
| 102 | two   |
+-----+-------+
2 rows in set (0.000 sec)

mysql97 15> SELECT * FROM t2;
+-----+-------+
| num | val   |
+-----+-------+
|   3 | three |
| 102 | two   |
+-----+-------+
2 rows in set (0.000 sec)

mysql97 15> SELECT * FROM log;
+-----+--------+---------------------+
| num | op     | dt                  |
+-----+--------+---------------------+
|   1 | DELETE | 2026-04-20 07:35:06 |
|   2 | UPDATE | 2026-04-20 07:35:08 |
+-----+--------+---------------------+
2 rows in set (0.000 sec)

使いどころがあるかどうか(インコンパチだから)は兎も角おもしろ(というか、今までできなかったこともあんまり気にしてなかった)

なお予告(?)通り、FOREIGN KEYのカスケードもトリガーの結果もちゃんとバイナリログに全部書くようになってた。

# at 3572
#260420  7:35:08 server id 1097  end_log_pos 3572 CRC32 0xf56f08ff      Rows_query
# UPDATE t1 SET num = 102 WHERE val = 'two'
# at 3572
#260420  7:35:08 server id 1097  end_log_pos 3572 CRC32 0x07966e20      Table_map: `d1`.`t1` mapped to number 130
# has_generated_invisible_primary_key=0
# at 3572
#260420  7:35:08 server id 1097  end_log_pos 3572 CRC32 0x7cbbaf93      Table_map: `d1`.`t2` mapped to number 133
# has_generated_invisible_primary_key=0
# at 3572
#260420  7:35:08 server id 1097  end_log_pos 3572 CRC32 0xf3902a3a      Table_map: `d1`.`log` mapped to number 128
# has_generated_invisible_primary_key=0
# at 3572
#260420  7:35:08 server id 1097  end_log_pos 3572 CRC32 0x3392556e      Update_rows: table id 133 flags: NO_FOREIGN_KEY_CHECKS_F UNKNOWN_FLAG(0x10)
# at 3572
#260420  7:35:08 server id 1097  end_log_pos 3572 CRC32 0xdecdd0e3      Write_rows: table id 128 flags: UNKNOWN_FLAG(0x10)
# at 3572
#260420  7:35:08 server id 1097  end_log_pos 3572 CRC32 0x58208ae7      Update_rows: table id 130 flags: STMT_END_F UNKNOWN_FLAG(0x10)

BINLOG '
LNflaR1JBAAAQQAAAAAAAACAAABVUERBVEUgdDEgU0VUIG51bSA9IDEwMiBXSEVSRSB2YWwgPSAn
dHdvJ/8Ib/U=
LNflaRNJBAAANgAAAAAAAAAAAIIAAAAAAAMAAmQxAAJ0MQACCA8CgAACAQGAAgP8/wAgbpYH
LNflaRNJBAAANgAAAAAAAAAAAIUAAAAAAAMAAmQxAAJ0MgACCA8CgAACAQGAAgP8/wCTr7t8
LNflaRNJBAAAOQAAAAAAAAAAAIAAAAAAAAMAAmQxAANsb2cAAwgPEgOAAAAGAQGAAgP8/wA6KpDz
LNflaR9JBAAAOgAAAAAAAAAAAIUAAAAAABIAAgACAf8AAgAAAAAAAAAAZgAAAAAAAAADdHdvblWS
Mw==
LNflaR5JBAAAOAAAAAAAAAAAAIAAAAAAABAAAgAD/wACAAAAAAAAAAZVUERBVEWZuah4yOPQzd4=
LNflaR9JBAAANgAAAAAAAAAAAIIAAAAAABEAAgACAQEAAgAAAAAAAAAAZgAAAAAAAADniiBY
'/*!*/;
### UPDATE `d1`.`t2`
### WHERE
###   @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### SET
###   @1=102 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='two' /* VARSTRING(128) meta=128 nullable=1 is_null=0 */
### INSERT INTO `d1`.`log`
### SET
###   @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @2='UPDATE' /* VARSTRING(128) meta=128 nullable=1 is_null=0 */
###   @3='2026-04-20 07:35:08' /* DATETIME(0) meta=0 nullable=1 is_null=0 */
### UPDATE `d1`.`t1`
### WHERE
###   @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### SET
###   @1=102 /* LONGINT meta=0 nullable=0 is_null=0 */
# at 3572
#260420  7:35:08 server id 1097  end_log_pos 3572 CRC32 0xbe416b78      Xid = 224
COMMIT/*!*/;

8.4だとこうなので、FKは書かれていないかった(し、トリガーは着火させるようにクエリを流してもトリガーぶんはバイナリログには書かれてない、これは従前といっしょ)

# at 515
#260420  7:32:22 server id 1084  end_log_pos 515 CRC32 0x46c03123       Rows_query
# UPDATE t1 SET num = 102 WHERE val = 'two'
# at 515
#260420  7:32:22 server id 1084  end_log_pos 515 CRC32 0x6de5ebb9       Table_map: `d1`.`t1` mapped to number 85
# has_generated_invisible_primary_key=0
# at 515
#260420  7:32:22 server id 1084  end_log_pos 515 CRC32 0x6a442810       Update_rows: table id 85 flags: STMT_END_F

BINLOG '
htblaR08BAAAQQAAAAAAAACAAClVUERBVEUgdDEgU0VUIG51bSA9IDEwMiBXSEVSRSB2YWwgPSAn
dHdvJyMxwEY=
htblaRM8BAAANgAAAAAAAAAAAFUAAAAAAAMAAmQxAAJ0MQACCA8CgAACAQGAAgP8/wC56+Vt
htblaR88BAAANgAAAAAAAAAAAFUAAAAAAAEAAgACAQEAAgAAAAAAAAAAZgAAAAAAAAAQKERq
'/*!*/;
### UPDATE `d1`.`t1`
### WHERE
###   @1=2 /* LONGINT meta=0 nullable=0 is_null=0 */
### SET
###   @1=102 /* LONGINT meta=0 nullable=0 is_null=0 */
# at 515
#260420  7:32:22 server id 1084  end_log_pos 515 CRC32 0x0f860ca6       Xid = 5
COMMIT/*!*/;

diffの全文はgistに上げたけど、WEBインターフェースだと後ろがちょん切れて見えないので(本当は33817行ある)ダウンロードしてみないといけなさそう。他に新機能っぽいものはないけどバグ直したついでのテストケースとか増えてる。

MySQL 5.7とそれ以前のpartial_revokesもどき

$
0
0

TL;DR

  • さっさとMySQL 8.0とそれ以降にアップグレードして partial_revokesを使う
  • 過去にはこんなテクニックがあったんだよという記憶だけ

日々の覚書: GRANTでデータベース名にワイルドカードを指定することとpartial revokesとでもちょっと書いていた、「GRANTでデータベース名にワイルドカード」を一捻りしたバージョン。

「一般ユーザーに(データベース名は動的に変わる、などの理由で)任意のスキーマに対するCREATE, DROPその他の権限を割り当てたいけど mysqlperformance_schemasysだけはダメ、グローバルGRANTはやりたくない」という、まさに partial_revokesが欲しい状況を5.7で何とかしたい。

mysql (とその他システム) スキーマには明示的な *_priv = 'N'を割り当てて、それ以外の %にはスキーマレベルでのALLをGRANTする。

mysql57 8> SELECT @@version;
+------------+
| @@version  |
+------------+
| 5.7.44-log |
+------------+
1 row in set (0.00 sec)

mysql57 8> CREATE USER yoku0825;
Query OK, 0 rows affected (0.00 sec)

mysql57 8> GRANT ALL ON `%`.* TO yoku0825 WITH GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)

mysql57 8> INSERT INTO mysql.db (host, db, user) VALUES ('%', 'mysql', 'yoku0825'), ('%', 'sys', 'yoku0825'), ('%', 'performance_schema', 'yoku0825');   -- *_privカラムのデフォルトは 'N'
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql57 8> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

「明示的な *_priv = 'N'の割り当て」はGRANTステートメントではできない ( GRANT USAGE ON mysql.* TO yoku0825とかやっても mysql.dbに全部 ‘N’ の行ができたりはしない)っぽいので、INSERTステートメントとFLUSH PRIVILEGESで表現する。

と、

mysql57 9> SHOW GRANTS;  -- 自分がmysqlスキーマとかに権限がないことが見えないのが嫌だといえば嫌だが (partial_revokesは見える)
+-------------------------------------------------------------------+
| Grants for yoku0825@%                                             |
+-------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'yoku0825'@'%'                              |
| GRANT ALL PRIVILEGES ON `%`.* TO 'yoku0825'@'%' WITH GRANT OPTION |
+-------------------------------------------------------------------+
2 rows in set (0.00 sec)

mysql57 9> SELECT COUNT(*) FROM mysql.user;
ERROR 1142 (42000): SELECT command denied to user 'yoku0825'@'localhost' for table 'user'

mysql57 9> SHOW TABLES FROM sys;
ERROR 1044 (42000): Access denied for user 'yoku0825'@'%' to database 'sys'

mysql57 9> CREATE DATABASE yoku0825;
Query OK, 1 row affected (0.00 sec)

mysql57 9> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| d1                 |
| world              |
| yoku0825           |
+--------------------+
4 rows in set (0.00 sec)

だいたいやりたいことができそう。
partial_revokesを有効にするとスキーマ名ワイルドカードが効かなくなるからできなくなるけど、そもそもつなぎのためのテクニックだろうからそれはそれで(アップグレードする時に忘れずに処置すれば)大丈夫なはず。

MySQL Shellのフルバックアップとmysqlbinlogを合わせてPITR

$
0
0

MySQL ShellのdumpInstanceとMySQL ShellのdumpBinlogsではなく MySQL ShellのdumpInstanceとmysqlbinlogの組み合わせでのPITR

テスト用コンテナの起動とテスト用のデータ作成とハートビートの書き込み。GTIDは有効な状態。

$ yt-sandbox 8.0
[3254174] NOTE: Generate Sandbox directry into /home/yoku0825/yt-sandbox/bravo
[3254174] NOTE: Node1 Container Ipaddress: 172.17.0.2
Sandbox deployed into /home/yoku0825/yt-sandbox/bravo

$ cd /home/yoku0825/yt-sandbox/bravo
$ ./n1 -e "CREATE DATABASE sbtest"

$ sysbench --mysql-host=172.17.0.2 --mysql-user=root oltp_read_write prepare --table-size=100000 --tables=10
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Creating table 'sbtest1'...
Inserting 100000 records into 'sbtest1'
Creating a secondary index on 'sbtest1'...
Creating table 'sbtest2'...
Inserting 100000 records into 'sbtest2'
Creating a secondary index on 'sbtest2'...
Creating table 'sbtest3'...
Inserting 100000 records into 'sbtest3'
Creating a secondary index on 'sbtest3'...
Creating table 'sbtest4'...
Inserting 100000 records into 'sbtest4'
Creating a secondary index on 'sbtest4'...
Creating table 'sbtest5'...
Inserting 100000 records into 'sbtest5'
Creating a secondary index on 'sbtest5'...
Creating table 'sbtest6'...
Inserting 100000 records into 'sbtest6'
Creating a secondary index on 'sbtest6'...
Creating table 'sbtest7'...
Inserting 100000 records into 'sbtest7'
Creating a secondary index on 'sbtest7'...
Creating table 'sbtest8'...
Inserting 100000 records into 'sbtest8'
Creating a secondary index on 'sbtest8'...
Creating table 'sbtest9'...
Inserting 100000 records into 'sbtest9'
Creating a secondary index on 'sbtest9'...
Creating table 'sbtest10'...
Inserting 100000 records into 'sbtest10'
Creating a secondary index on 'sbtest10'...

$ yt-heartbeat -h 172.17.0.2 -uroot -v

まずはMySQL Shellの util.dumpInstanceでフルバックアップを取る。

$ date ; mysqlsh mysql://root@172.17.0.2 --js -- util dumpInstance '/tmp/test' ; date
Thu May  7 07:43:06 GMT 2026
Please provide the password for 'root@172.17.0.2':
Save password for 'root@172.17.0.2'? [Y]es/[N]o/Ne[v]er (default No):
Acquiring global read lock
Global read lock acquired

..
Uncompressed data size: 191.90 MB
Compressed data size: 87.48 MB
Compression ratio: 2.2
Rows written: 1000092
Bytes written: 87.48 MB
Average uncompressed throughput: 191.90 MB/s
Average compressed throughput: 87.48 MB/s
Thu May  7 07:43:10 GMT 2026

dumpInstanceは論理バックアップなので、 START TRANSACTION WITH CONSISTENT SNAPSHOTを使っている。よって、このバックアップをリストアした時に復旧できるタイムスライスは「バックアップを開始した時刻」になる。これはバックアップ先の @.jsonに入っていそう。

$ jq -r .begin /tmp/test/@.json
2026-05-07 07:43:09

もう1個サンドボックスを立ち上げてリストアしてみる。 updateGtidSet=replaceにしないと新しいGTIDを払い出しちゃうので指定する。

$ yt-sandbox 8.0
[3255163] NOTE: Generate Sandbox directry into /home/yoku0825/yt-sandbox/charlie
[3255163] NOTE: Node1 Container Ipaddress: 172.17.0.3
Sandbox deployed into /home/yoku0825/yt-sandbox/charlie

$ cd /home/yoku0825/yt-sandbox/charlie
$ ./n1 -e "SET GLOBAL local_infile = ON"

$ date ; mysqlsh mysql://root@172.17.0.3 --js -- util loadDump '/tmp/test' { --updateGtidSet=replace } ; date
Thu May  7 07:49:37 GMT 2026
Please provide the password for 'root@172.17.0.3':
Save password for 'root@172.17.0.3'? [Y]es/[N]o/Ne[v]er (default No):
Loading DDL and Data from '/tmp/test' using 4 threads.

..
Resetting GTID_PURGED to dumped gtid set
11 chunks (1.00M rows, 191.90 MB) for 11 tables in 2 schemas were loaded in 23 sec (avg throughput 8.16 MB/s, 42.54K rows/s)
13 DDL files were executed in 0 sec.
Data load duration: 23 sec
Total duration: 23 sec
0 warnings were reported during the load.

Thu May  7 07:50:04 GMT 2026

$ ./n1 -e "SELECT hostname, server_time FROM ytkit.heartbeat ORDER BY server_time DESC LIMIT 1"  -- 07:43:09.546 のデータが手に入った
+----------+-------------------------+

| hostname | server_time             |
+----------+-------------------------+
| bravo-1  | 2026-05-07 07:43:09.546 |
+----------+-------------------------+

↑うーん、ミリ秒まで @.jsonに入っていてほしい気もする…。

あとはmysqldumpの時と同じく、「リストア後のGTIDが歯抜けにならないように」(= この場合は「少なくとも必ず b55fb915-49e7-11f1-87a5-0242ac110002:495とそれ以降のGTID」を含む)バイナリログを適用すればいい。

rsyncか何かで定期的にバイナリログを他の場所に移しておいて(cpで代用)

$ mkdir work
$ sudo cp -ip /home/yoku0825/yt-sandbox/bravo/node1/datadir/binlog.00000* work/
$ ll work/
total 186720
-rw-r-----. 1 mysql mysql       180 May  7 07:38 binlog.000001
-rw-r-----. 1 mysql mysql       180 May  7 07:38 binlog.000002
-rw-r-----. 1 mysql mysql 191192963 May  7 07:54 binlog.000003

mysqlbinlogの —stop-datetimeで着地したい時間を指定しつつ mysql コマンドラインクライアントに食わせる。

GTIDモードなので二重適用を恐れる必要はなく、邪魔にならない程度(無視されるとはいえ、GTIDをチェックして空振りさせるので多少の時間は必要で、ぴったり495から始める自信があるなら495から始めても良い)にgtid_executedがオーバーラップするように適用させる。

↑の例だと俺なら binlog.000001から適用してしまう。000001と000002は実質空っぽだし、000003の07:43:09.546以前のイベントは単に読み捨てられるので。

$ sudo mysqlbinlog --stop-datetime="2026-05-07 07:50:03" work/* | mysql -h172.17.0.3 -uroot

$ ./n1 -e "SELECT hostname, server_time FROM ytkit.heartbeat ORDER BY server_time DESC LIMIT 1"
+----------+-------------------------+
| hostname | server_time             |
+----------+-------------------------+
| bravo-1  | 2026-05-07 07:50:02.848 |
+----------+-------------------------+

$ ./n1 -e "SHOW MASTER STATUS"
+---------------+-----------+--------------+------------------+---------------------------------------------------------------------------------------+
| File          | Position  | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                                                                     |
+---------------+-----------+--------------+------------------+---------------------------------------------------------------------------------------+
| binlog.000001 | 190977368 |              |                  | b55fb915-49e7-11f1-87a5-0242ac110002:1-904,
ecc7abed-49e8-11f1-ad40-0242ac110003:1-24 |
+---------------+-----------+--------------+------------------+---------------------------------------------------------------------------------------+

mysqlbinlogの --stop-datetimeは当該時刻「以上」のタイムスタンプ(秒まで)が現れた時点でbreakするので、7:50:03.855860のgitd=’b55fb915-49e7-11f1-87a5-0242ac110002:905’ は適用されない。

$ sudo mysqlbinlog -vv work/binlog.000003 | less
..
# at 191101220
#260507  7:50:03 server id 201  end_log_pos 191101299 CRC32 0xaa35a563  GTID    last_committed=904      sequence_number=905     rbr_only=yes    original_committed_timestamp=1778140204080496   immediate_commit_timestamp=1778140204080496     transaction_length=362
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=1778140204080496 (2026-05-07 07:50:04.080496 GMT)
# immediate_commit_timestamp=1778140204080496 (2026-05-07 07:50:04.080496 GMT)
/*!80001 SET @@session.original_commit_timestamp=1778140204080496*//*!*/;
/*!80014 SET @@session.original_server_version=80046*//*!*/;
/*!80014 SET @@session.immediate_server_version=80046*//*!*/;
SET @@SESSION.GTID_NEXT= 'b55fb915-49e7-11f1-87a5-0242ac110002:905'/*!*/;
# at 191101299
#260507  7:50:03 server id 201  end_log_pos 191101382 CRC32 0x768811b1  Query   thread_id=13    exec_time=0     error_code=0
SET TIMESTAMP=1778140203.855860/*!*/;
BEGIN
/*!*/;
# at 191101382
#260507  7:50:03 server id 201  end_log_pos 191101448 CRC32 0x2ce58d7d  Table_map: `ytkit`.`heartbeat` mapped to number 145
# has_generated_invisible_primary_key=0
# at 191101448
#260507  7:50:03 server id 201  end_log_pos 191101551 CRC32 0xde01d932  Write_rows: table id 145 flags: STMT_END_F

BINLOG '
K0T8aRPJAAAAQgAAAAj6YwsAAJEAAAAAAAEABXl0a2l0AAloZWFydGJlYXQABA8SEvwF/AMDAwIA
AgP8/wB9jeUs
K0T8aR7JAAAAZwAAAG/6YwsAAJEAAAAAAAEAAgAE/wAHAGJyYXZvLTGZuc58gyFwmbnOfIMhZioA
YjU1ZmI5MTUtNDllNy0xMWYxLTg3YTUtMDI0MmFjMTEwMDAyOjEtOTA0MtkB3g==
'/*!*/;
### INSERT INTO `ytkit`.`heartbeat`
### SET
###   @1='bravo-1' /* VARSTRING(1020) meta=1020 nullable=0 is_null=0 */
###   @2='2026-05-07 07:50:03.856' /* DATETIME(3) meta=3 nullable=0 is_null=0 */
###   @3='2026-05-07 07:50:03.855' /* DATETIME(3) meta=3 nullable=0 is_null=0 */
###   @4='b55fb915-49e7-11f1-87a5-0242ac110002:1-904' /* BLOB/TEXT meta=2 nullable=0 is_null=0 */
# at 191101551
#260507  7:50:03 server id 201  end_log_pos 191101582 CRC32 0xaa63b7d5  Xid = 16673
COMMIT/*!*/;

..

とこんな感じ。



Latest Images