JdbcRunner 1.3.1リリース(データベース負荷テストツール)

JdbcRunner 1.3.1

JdbcRunner 1.3.1をリリースしました。JdbcRunnerは各種RDBMSを対象としたオープンソースの負荷テストツールです。スクリプトトランザクションを定義して多重実行し、スループットとレスポンスタイムを測定できます。またJdbcRunnerにはOracle Database、MySQLPostgreSQLを対象としたテストキットが付属しており、ユーザーが独自にスクリプトを作成する以外にこれらを用いたベンチマークを行うことも可能です。

dbstudy.info

JdbcRunner 1.3.1では最新環境への追従と細かい不具合修正を行いました。機能追加はありません。

  • 動作要件をJava 8 → Java 17へ変更
  • PostgreSQL JDBC Driverを42.6.0へ更新
  • MySQL Connector/Jを8.0.32へ更新
  • MySQL Connector/J 8.0.27以降でANALYZE文をexecute()で実行できない件に対応

以下のRDBMSで動作を確認しています。

それぞれのRDBMSにおける注意点

久しぶりにツールを動かしてみて、つまづいたところを共有します。

Oracle Databaseのコンテナーイメージを使う場合は-Doracle.net.disableOob=trueを指定する

Oracle Database 19c以降では、クエリーのキャンセルにTCPのURGフラグを使用しています。

参考:第15回 信頼性のある通信を実現するTCPプロトコル(2):基礎から学ぶWindowsネットワーク(2/3 ページ) - @IT

ここで、ルーターやプロキシーによってはURGフラグを正常に処理できずに通信エラーを引き起こすものがあります。残念ながらDocker、Podmanは条件に該当してしまうので、コンテナーイメージを使う場合はツールの起動オプションに-Doracle.net.disableOob=trueを指定してください。

参考:docker-images/FAQ.md at main · oracle/docker-images · GitHub

shell> java -Doracle.net.disableOob=true JR tpcc.js ...

MySQL Connector/J 8.0.27以降ではANALYZE文をquery()で実行する

MySQL Connector/J 8.0.27における不具合修正で、結果セットを返すSQLはexecuteQuery()、結果セットを返さないSQLはexecuteUpdate()で実行するように明確化されました。JdbcRunnerのスクリプトではそれぞれquery()とexecute()に対応しています。

MySQLのANALYZE文は結果セットを返すので、MySQL Connector/J 8.0.27以降ではANALYZE文をexecute()で実行できなくなりました。JdbcRunnerのスクリプトでexecute("ANALYZE TABLE sbtest");という箇所がある場合はquery("ANALYZE TABLE sbtest");への書き換えをお願いいたします。OPTIMIZE文も同様です。ツール付属のテストキットは修正済みです。

PostgreSQLでは極力publicスキーマを使わない

PostgreSQL 15以降ではpublicスキーマに対するCREATE権限がデータベース所有者以外には付与されなくなりました。これはCVE-2018-1058に対処したものです。今後は極力publicスキーマを使わずにデータベースユーザーごとにスキーマを作成することが推奨されます。

参考:A Guide to CVE-2018-1058: Protect Your Search Path - PostgreSQL wiki

shell> psql -U postgres
sql> CREATE DATABASE sbtest TEMPLATE template0 ENCODING 'UTF-8' LC_COLLATE 'C' LC_CTYPE 'C';
sql> CREATE USER sbtest PASSWORD ...;
shell> psql -U postgres sbtest
sql> CREATE SCHEMA AUTHORIZATION sbtest;

今後の予定

JdbcRunnerに機能追加の予定は特にありませんが、今後はJavaのLTSバージョンに追従する形でメンテナンスを続けていければと考えています。

RequestDisplayOn - スクリーンセーバーを抑止する

RequestDisplayOn

RequestDisplayOnという小さなアプリケーションをリリースしました。コンピューターを長時間操作していないときに、スクリーンセーバーが起動したりディスプレイがオフになったりすることを抑止するコンソールアプリケーションです。Windows 10 Version 1809以降に対応しています。

  • バイナリ

WindowsのノートPCでLinuxサーバにSSH接続して作業をしているとき、少し席を外しただけで接続が切れてしまって何度も困ったので作成しました。ノートPCの電源オプションを都度調節すればよいのですが私は変更したことを忘れてしまうので…。

もし似たような状況で困っている方がいらっしゃいましたら試してみていただければと思います。このアプリケーションはインストーラーなし、ランタイムライブラリなし、管理者権限なしで動くように作ってあります。

SnapパッケージをNFS上で起動する場合はAppArmorに注意

UbuntuなどのLinuxディストリビューションで、比較的新しいソフトウェアをインストールするためにSnapパッケージを利用することがあるかと思います。このSnapパッケージについてNFSマウントしたディレクトリ上では起動しないという不具合に遭遇したので、原因と暫定対処策をメモしておきます。Ubuntu 22.04 LTSで確認しています。

例としてKotlinのSnapパッケージをインストールしてみます。

~$ sudo snap install kotlin
…

~$ kotlin -version
Kotlin version 1.7.21-release-272 (JRE 17.0.5+8-Ubuntu-2ubuntu122.04)

~$ cd /nfs
/nfs$ kotlin -version
cannot open path of the current working directory: Permission denied

このようにNFS上ではKotlinが起動しません。原因はAppArmorがプロセスをブロックしてしまっているためで、syslogでその様子を確認できます。

Jan 24 22:08:09 ubuntu2204-01 systemd[1099]: Started snap.kotlin.kotlin.2c2e7cf1-5e19-43f0-853f-0f1eee666559.scope.
Jan 24 22:08:09 ubuntu2204-01 kernel: [   76.634145] nfs: RPC call returned error 13
Jan 24 22:08:09 ubuntu2204-01 kernel: [   76.634166] kauditd_printk_skb: 31 callbacks suppressed
Jan 24 22:08:09 ubuntu2204-01 kernel: [   76.634168] audit: type=1400 audit(1674565689.275:43): apparmor="DENIED" operation="sendmsg" profile="/snap/snapd/17950/usr/lib/snapd/snap-confine" pid=1336 comm="snap-confine" laddr=192.168.1.148 lport=800 faddr=192.168.1.2 fport=2049 family="inet" sock_type="stream" protocol=6 requested_mask="send" denied_mask="send"

以下のような仕組みになっているようです。

  • Snapパッケージはsnap-confineというコマンド経由で起動される
  • snap-confineはAppArmorによってネットワーク処理が禁止されている
  • NFS上ではファイルアクセスがネットワーク処理だと解釈されてブロックされてしまう

関連リンクです。

  • AppArmorがNFSアクセスをネットワーク処理として取り扱うことが間違いではないか?という報告。2023年1月時点であまり進展はありません。
  • Snap側でNFS上でも起動するように対処するべきではないか?という議論。ホームディレクトリ自体をNFSマウントしている環境で困っている人が多いようです。


暫定対処策です。AppArmorを丸ごと無効化してしまいたくなるところですがそれでは影響が大きすぎるので、さしあたりsnap-confineにネットワーク処理を許可することにします。

syslogを見るとAppArmorに /snap/snapd/17950/usr/lib/snapd/snap-confine というプロファイルが登録されていることが分かります。このプロファイルの設定は /var/lib/snapd/apparmor/profiles に格納されています。ちなみに17950というのはsnapdの内部的なリビジョン番号を表しています。

設定ファイルの末尾にnetwork,を追加します。カンマを付け忘れると動きませんので注意してください。

~$ sudo vim /var/lib/snapd/apparmor/profiles/snap-confine.snapd.17950
…
    deny /etc/nsswitch.conf r,
    deny /etc/passwd r,

    network,
}

snapd.apparmorサービスを再起動します。

~$ sudo systemctl restart snapd.apparmor

これでNFS上でもSnapパッケージが起動するようになりました。

~$ cd /nfs
/nfs$ kotlin -version
Kotlin version 1.7.21-release-272 (JRE 17.0.5+8-Ubuntu-2ubuntu122.04)

注意点として、Snapパッケージを追加、削除するとプロファイルが上書きされて元に戻ってしまうため、プロファイルを再度編集することになる点があります。暫定対処策なので、これくらいは仕方ないかなと考えています。

JdbcRunner 1.3リリース(データベース負荷テストツール)

かなり昔に紹介したJdbcRunnerを、7年ぶりに更新しました。JdbcRunnerは各種データベースを対象とした負荷テストツールで、スクリプトトランザクションを定義して多重実行し、スループットとレスポンスタイムを測定することができます。レスポンスタイムはきれいにグラフ化もできます。

Oracle Database 18c、MySQL 8.0、PostgreSQL 10といった現行バージョンへの追従がメインです。Oracle Database 18cとPostgreSQL 10は昔のままでも動いたのですが、MySQL 8.0に接続するためにConnector/J 8.0が推奨されていて、Connector/J 8.0を動かすためにはJava SE 8が必須で…、という状況でしたので諸々更新しておきました。また、いくつか細かな不具合も修正しています。

大きく手を入れる機会があればJavaScriptエンジンをMozilla Rhinoからより高速なNashornに入れ替えたかったのですが、意外と非互換があるのと、残念ながらNashornがDuplicatedになってしまうとのことで、見送ることにしました。

このツールを最初に作っていたときはWindows XPEclipse 3.4を使って、ソースコード自宅サーバSubversionで管理していました。それからWindowsは4バージョン、Eclipseは9バージョン上がり、ソースコードGitHubに移行し、動作確認用のRDBMSはDockerで動かすようになり、あと最近4Kのモニタを導入しまして(^^;、だいぶ快適になりました。私は普段あまりプログラミングをしていないので何かしようとするたびに開発環境から作り直しているような状態ですが、今後もバージョンアップ追従は続けていこうと思います。

dstat2graphs(dstatグラフ化ツール)の更新

5年前に作ったdstat2graphsを更新しました。

  • RHEL 7系に対応し、RHEL 5系の対応を終了しました。
  • dstatのオプション -r(Disk IOPS) と -l(Load Average) に対応しました。
  • 任意の取得間隔秒数に対応しました。
  • X軸に経過時間を表示するか実際の時刻を表示するかを選べるようにしました。

OSのリソース情報を収集する際、本番環境であればZabbix、Elastic Stackなどの監視ツールを使うところですが、試験環境でしたら手軽にdstatで済ませるのも一つの案かと思います。dstatはディストリビューションに付属しているので、導入の壁が低いのもうれしいですね。
dstat2graphsもなかなか自由にソフトウェアの導入ができない開発現場を想定して、ディストリビューション付属のパッケージのみで構築できるようにしています。ぜひ、試してみてください。

(おまけ)iostat2graphsとrstat

dstatはとても便利なのですが、DBエンジニアとしてはディスクI/Oをもう少し詳しく調査したいところです。そこでiostatを使うわけですが、せっかくなのでiostatもグラフ化できるようにしておきました。

iostatはdstatほどmachine-readableなログを出力してくれないので、あらかじめフィルタを挟む設計にしました。リソース情報を収集する際はiostatを直接実行するのではなく、以下のrstatというツールを使用してください。

rstatはdstat、iostatとついでにpidstatを複数のリモートホストで実行し、ログをCSV形式で出力するツールです。出力されたdstatのログはdstat2graphs、iostatのログはiostat2graphsでグラフ化することができます。pidstatに対しては特にツールは用意しておらず、Excelでオートフィルタをかけて見ることを想定しています。

MySQLのロックについて

JPOUG> SET EVENTS 20140907 | Japan Oracle User Group (JPOUG)に参加して発表をしてきました。IIJさまのセミナルームは窓からの眺めがすばらしいですね。JPOUGの運営メンバのみなさま、会場を提供してくださったIIJのみなさま、当日お越しいただいたみなさま、どうもありがとうございました。

私のセッションでは「MySQLのロックについて」と題してネクスキーロックなどの説明をしました。プレゼンテーション資料と、調査のために作成したツールを公開します。


プレゼンテーション資料からリンクしているウェブサイトの一覧です。

過去記事の訂正

@kamipoさんから言及がありましたが、私は2009年の記事でネクスキーロックという用語を誤って使用していました。

ところで、ネクスキーロックというとsh2さんのMySQL InnoDBのネクストキーロック おさらい - SH2の日記の記事が有名ですよね。この、ひとつ先のインデックスレコードまでロックするのもネクスキーロックと呼ぶし、レコードロックとその直前のギャップロックの組み合わせもネクスキーロックと書いてるし、議論するときにはどちらの意味で使ってるのか文脈読み取れる社会性が必要そうです(今回はレコードロックとその直前のギャップロックの組み合わせの意味で使います)。

  • 誤:一つ先のレコードまでロックを取得すること。
  • 正:レコードとその手前のギャップに対するロックのこと。

ご指摘ありがとうございました。

訂正前

1つめのセッションではc1 < 30の行だけロックをすればいいのですが、実際にはc1 = 30の行もロックされてしまっています。これはInnoDBアーキテクチャからもたらされている制限事項で、このロックのことをネクスキーロックといいます。ある範囲をロックする際に、一つ先の行までロックをかけることで「範囲」というものを表現する仕組みです。

訂正後

1つめのセッションではc1 < 30の行だけロックをすればいいのですが、実際にはc1 = 30の行もロックされてしまっています。これはInnoDBアーキテクチャからもたらされている制限事項です。InnoDBはインデックス上で走査した行に対してロックをかけるアーキテクチャとなっており、このケースではc1 = 30の行まで走査しています。また、走査したそれぞれの行に対してネクスキーロックと呼ばれる特殊なロックをかけています。ネクスキーロックとは行とその手前のギャップに対するロックのことで、現時点で存在しない行に対してロックをかける現実的な仕組みです。InnoDBはこのような仕組みで「範囲」というものを表現し、ファントムリードを防いでいるのです。

MySQL Casual Talks vol.6の復習

今回の資料は、MySQL Casual Talks vol.6での@karupaneruraさんの発表にインスパイアされて作成しました。

じっくり復習すると、P20はセカンダリインデックスでcol1 = 8の手前に対するギャップロックが取得されていること、P21以降で(gap)は最初と最後だけではなく途中にも存在していること、などが分かるかと思います。

Lock Inspector

今回の資料を作るにあたって、簡単なツールを作成しました。以下のようなスクリプトを準備すると、

1:RC
2:RC
1:Q:SELECT * FROM emp WHERE empno = 7788 FOR UPDATE
2:Q:SELECT * FROM emp WHERE empno = 7788 FOR UPDATE
S:10
1:C
2:C

複数のワーカがコマンドを逐次発行してくれます。

Lock Inspector
1:READ_COMMITTED
2:READ_COMMITTED
1:QUERY:SELECT * FROM emp WHERE empno = 7788 FOR UPDATE
(empno      ename      job        mgr        hiredate   sal        comm       deptno     )
(7788       scott      analyst    7566       1987-04-19 3000.00    null       20         )
(1:QUERY)
2:QUERY:SELECT * FROM emp WHERE empno = 7788 FOR UPDATE
SLEEP:10
(2:QUERY)
(2:com.mysql.jdbc.exceptions.MySQLTimeoutException: Statement cancelled due to timeout or client request)
2:ABORT
(SLEEP)
1:COMMIT
1:EXIT

GitHub上は、sandboxというあまりやる気のないリポジトリに入れてあります。

トランザクション処理 概念と技法

参考書籍です。洋書は購入可能です。

Transaction Processing: Concepts and Techniques (The Morgan Kaufmann Series in Data Management Systems)

Transaction Processing: Concepts and Techniques (The Morgan Kaufmann Series in Data Management Systems)

和書は中古で手に入れるしかないと思います。こちらは上下巻に分かれています。
トランザクション処理 上

トランザクション処理 上

トランザクション処理 下

トランザクション処理 下

MySQL 5.1のプロダクトライフサイクルが終了

2013年12月31日をもってMySQL 5.1のプロダクトライフサイクルが終了しました。今後MySQL 5.1に対して新たな不具合や脆弱性が見つかっても、開発元による修正は行われません。現在もMySQL 5.1を使用している場合は、MySQL 5.5/5.6へ計画的にバージョンアップをされることをおすすめいたします。

リリース GA日 Premier Support終了 Extended Support終了 Sustaining Support終了
MySQL 5.0 2005年10月 2011年12月 Not Available Indefinite
MySQL 5.1 2008年12月 2013年12月 Not Available Indefinite
MySQL 5.5 2010年12月 2015年12月 2018年12月 Indefinite
MySQL 5.6 2013年2月 2018年2月 2021年2月 Indefinite

(Lifetime Support Policy, Coverage for Oracle Technology Products - November, 2013より引用)

古いバージョンの頒布

2年前MySQL 3.23〜5.0までのバージョンを集めたウェブサイトを作成したのですが、そこにMySQL 5.1を追加しました。

現時点で公式サイトからはMySQL 5.0以降のバージョンをダウンロードすることが可能です。そのため今回追加したMySQL 5.1が今すぐ役に立つわけではありませんが、公開が終了した際にはご利用いただければと思います。

バグ曲線

こちらも2年前に調べたのですが、MySQL 5.6を追加して更新しました。

MySQL 5.6はGA後もバグ修正数が勢いよく伸びていて、MySQL 5.0と近い傾向にあるようです。一方MySQL 5.5はかなり安定しています。