maatkitを一通り使ってみたメモ

#hbstudy11id:marqsさんがMaatkitに関する発表をしていて,僕も仕事でちょこちょこ使っていたので

ダイアリーあたりに書きますね

と云ったきり,書く書く詐欺になっていたので,さすがに書こうと思います.
割とみなさん知っているツールだと思うのですが,ウェブ上で日本語の情報がなかなか見つからないので,何かのお役に立てればと思います.というか英語読めってことなのかもしれませんが.

Mattkit

Maatkitは「実践ハイパフォーマンスMySQL」の著者であるBaron Scheartzによって作り始められた,MySQLPostgreSQLのようなオープンソースのデータベースのための高品質なコマンドラインツールです.

実践ハイパフォーマンスMySQL 第2版

実践ハイパフォーマンスMySQL 第2版

Maatkit is a set of high-quality command-line tools for open-source databases, such as MySQL and PostgreSQL. The tools enable power users to do their work more safely and quickly by relying on tested software instead of scripting things themselves. The toolkit includes functionality such as table checksums, a query profiler, and a visual EXPLAIN tool. It also provides missing features such as checking whether slaves have the same data as the master.

Google Code Archive - Long-term storage for Google Code Project Hosting.

このツールはユーザが自分たちで書いたスクリプトよりも安全かつ迅速にテストされたソフトウェアで作業を行うユーザのために力を発揮します.
これらのツール群はテーブルのチェックサムやクエリのプロファイラ,EXPLAINの視覚化などのツールがあったり,スレーブがマスタと同様のデータを持っているかどうかをチェックするというような機能も提供してくれます.
みたいなことを書いてありますが,一言でいうと"MySQLとか絡みの作業で超便利なツール"です.

うちでも便利で,作業がたんぽぽ仕事になりそうだと思った時にアシストしてくれるのが助かります.
全部Perlで書かれているので,中身どうなってんの?と不安になったらコードを読めば少しは安心出来ますし,実行中に何をやっているかをダンプさせることも出来ます.(MKDEBUG=1 をコマンドの頭に付けてみて下さい)

代表的なものとしては

  • レプリケーションをぶらさげ変えたり(mk-slave-move)
  • 特定のエラーを無限スキップしてレプリを再開させたり(mk-slave-restart)
  • テーブルの内容を同期したり(mk-table-sync)

といったツールに助けられることが度々あります.

インストール方法

rpm,deb含め置いてあるので,ダウンロードしてよしなにして下さい.
rpmで最初に試したのですが,一部CPANモジュールを求められます.MacOSXでtar.gz落として来てインストールして使う分には今のところ困ったことが起きていません.あ,でもパスワードなど対話的入力が出来ない状態なのはそのせいかもしれません.

設定

  • /etc/maatkit/maatkit.conf
  • ~/.maatkit.conf

user=root
socket=/var/lib/mysql/mysql.sock
password=hxmasaki

などと書くことで,mysqlへの接続の設定を保存しておくことが出来ます.
また,ツールごとの設定として

  • /etc/maatkit/.[ツール名].conf
  • ~/.[ツール名].conf

というのも作っておけます.
どちらもホームディレクトリにあるものが優先されます.

各ツール紹介

現在全部で26もツールがあるので,それぞれ概要と実際に実行した例をかいつまんでお送りします.詳しく知りたい場合はmanが全て準備されているので細かいオプション等はそちらで調べてみて下さい.

mk-archiver mk-error-log mk-kill mk-parallel-restore mk-show-grants mk-slave-prefetch mk-upgrade
mk-checksum-filter mk-fifo-split mk-loadavg mk-profile-compact mk-slave-delay mk-slave-restart mk-visual-explain
mk-deadlock-logger mk-find mk-log-player mk-query-digest mk-slave-find mk-table-checksum
mk-duplicate-key-checker mk-heartbeat mk-parallel-dump mk-query-profiler mk-slave-move mk-table-sync

この数を改めて見るとちょっと気が滅入ります.
まだ試していないものもあるので,やってみたら順次更新していこうと思います.実行結果や活用方法大歓迎です.

mk-archiver

条件に当てはまる行単位のデータを他のテーブルやファイルにアーカイブするツール.
指定条件でのロギングとかバックアップやデータのパージがしやすいです.

[hxmasaki@hmsk maatkit]$ mk-archiver --source h=192.168.10.210,D=service,t=comments --file '/home/hxmasaki/maatkit/comments_archive-%Y-%m-%d-%D.%t' --no-delete --where 'id < 100'

とすると,
指定したデータベース,テーブル,条件の行を削除せずにファイルにアーカイブする.(192.168.10.210 のserviceデータベース,commentsテーブルでidが100未満の行)
出力されたファイルはタブ区切り,改行コードが\Nと何故だか大文字で出る...

    • fileでなく--destとするとファイルではなく他のデータベースへ出力することも出来ます.

--no-deleteを指定しないとサーバへDELETEが走って見事にアーカイブされてしまうので気をつけましょう.
付けずに実行して,元に戻すのに一度泣きました.

mk-deadlock-logger

データベースのデッドロック情報をダンプするツール.

    • daemonize --run-time 4h --interval 30s というようなオプションも指定可能なので監視とかに使えそうです.
    • dest を指定してデッドロックデータベースを作って記録させるなど出来ます.

デッドロックな場面に出くわしたことがないので,出力が貼付けられません.情報希望.

mk-duplicate-key-checker

テーブルのインデックスや外部キーの重複を探すツール.
ちょっとくらいのインデックス数,テーブル数だったら目視でチェック出来ると思いますが,膨大になってくるとそれも大変な上,開発者は気づかずインデックスを重複して増産しているかもしれません.

[hxmasaki@hmsk maatkit]$ mk-duplicate-key-checker -h192.168.10.210
(中略)
# ########################################################################
# service.search                                             
# ########################################################################

# tagindex is a left-prefix of parent_id_position
# Key definitions:
#   KEY `newindex` (`parent_id`),
#   KEY `parent_id_position` USING BTREE (`parent_id`,`position`),
# Column types:
#         `parent_id` int(11) default null
#         `position` int(11) default null
# To remove this duplicate index, execute:
ALTER TABLE `service`.`search` DROP INDEX `tagindex`;

# ########################################################################
# Summary of indexes                                                      
# ########################################################################

# Size Duplicate Indexes   183900
# Total Duplicate Indexes  3
# Total Indexes            120

という感じで,親切にその重複したキーを消すクエリまで返してくれる.

mk-error-log

エラーログを集計したり分類したりして見やすく出してくれるツール.
.errファイルを食べさせると,まとめて吐いてくれる.

[hxmasaki@hmsk maatkit]$ mk-error-log db-master.err
Count Level   Message
===== ======= ================================================================
   10 info    mysqld started
    9 info    mysqld ended
    9 warning [Warning] 'user' entry 'root@db-master' ignored in --skip...
    9 warning [Warning] 'user' entry '@db-master' ignored in --skip-nam...
    9 info    mysqld version info
    9 info    [Note] /usr/local/mysql/bin/mysqld: ready for connections.
    8 info    [Note] /usr/local/mysql/bin/mysqld: Shutdown complete
    8 info    [Note] /usr/local/mysql/bin/mysqld: Normal shutdown
    8 info    [Note] Error reading relay log event: slave SQL thread was ki...
    7 info    [Note] Slave SQL thread initialized, starting replication in ...
    4 info    [Note] Slave I/O thread exiting, read up to log 'log-bin.0000...
    4 warning [Warning] Neither --relay-log nor --relay-log-index were used...

あまり,活用出来る場面を想像出来ませんでした.

mk-fifo-split

ファイル分割をせずに大きなファイルを見られるようにするツール(裏で分割されてるけど)

[hxmasaki@hmsk maatkit] # mk-fifo-split --lines 10000 db-master-slow.log 

としておいて,別コンソールから

[hxmasaki@hmsk maatkit] # while [ -e /tmp/mk-fifo-split ]; do cat /tmp/mk-fifo-split; done

とかやって10000行でchunkされたデータを手に入れられます.これが軽いのかどうかはよく調べていないです.
スローログが10000行以上?という突っ込みは無しで.も,もちろん閾が0なだけですよ!

mk-find

GNU findコマンドっぽくテーブル検索したりexecを付けて処理を行えるツール.

  • 5GB以上のテーブルを調べたり
[hxmasaki@hmsk maatkit]$ mk-find -h192.168.10.210 --tablesize +5G
`service`.`logs`
`service`.`comments`
`batch`.`pvs`
`batch`.`uus`
  • 空になっている指定データベースのテーブルを探したり
[hxmasaki@hmsk maatkit]$ mk-find -h192.168.10.210 summarized_data --empty
`batch`.`100401_pvs`
`batch`.`100402_pvs`
  • テーブルサイズ順にprintf出力させてみたり
[hxmasaki@hmsk maatkit]$ mk-find -h192.168.40.210 --printf "%T\t%D.%N\n"|sort -rn | head
14625908856     `batch`.`daily_pvs`
12677246456     `batch`.`daily_extend_pvs`
10142474608     `service`.`logs`
7027878208      `service`.`refers`
4108927800      `service`.`comments`
3203273640      `service`.`memos`

と,工夫次第で便利なことが出来そうです.

mk-heartbeat

レプリケーションの遅れをモニタするツール.
show slave statusは正しい情報じゃないから信用するなとハイパフォ本には書いてありました.笑
仕組み的には親でタイムスタンプを打ったデータを子にレプリケーションして時間を比較して,遅延時間を出力しています.
但しタイムスタンプ記録のために heartbeat用のテーブルが必要です.

    • create-tableというオプションを付けることで以下のような必要なテーブルを作ってくれます.
CREATE TABLE heartbeat (
              id int NOT NULL PRIMARY KEY,
              ts datetime NOT NULL
            );

モニタリングはレプリケーションのmasterで--updateオプションを付けて実行しているときにslaveで--monitorオプションを立てたものを実施することで実現されます.

  • @master
[hxmasaki@hmsk maatkit]$ mk-heartbeat -h192.168.10.209 -D test --update

をしておくと,毎秒でtest.heartbeatが更新されていく.

  • @slave
[hxmasaki@hmsk maatkit]$ mk-heartbeat -h192.168.10.210 -D test --monitor
  -1s [ -0.02s, -0.00s, -0.00s ]
  -1s [ -0.03s, -0.01s, -0.00s ]
  -1s [ -0.05s, -0.01s, -0.00s ]
  -1s [ -0.07s, -0.01s, -0.00s ]
  -1s [ -0.08s, -0.02s, -0.01s ]

数字は移動平均法を使っているらしい,移動値が括弧内の左端なのはわかるのですが,出力の内容を未だ把握していません.
(レプリが追いつきっぱなしなので例が悪いですが遅れが起きるような環境下でさすがに試していません)標準出力なのでファイルに書くか,--fileを指定してファイルに吐かせるなど出来ます.また,--monitorでなく--checkとすることで実行時の遅れ時間だけを表示させることも出来ます.
test.heartbeat自体のレプリ遅れを見ることで全体の遅れがわかるのかどうか微妙な気分になりますが,show slave statusの瞬間的なものよりは遅延具合がわかりそうな気はしますね.

mk-kill

条件一致したクエリを殺してくれたり,見せてくれたりするツール.

  • 1秒以上実行しているクエリやコネクションを抽出する
[hxmasaki@hmsk ~]$ mk-kill -h192.168.10.20 --busy-time 1 --print
# 2010-05-21T18:45:25 KILL 886251 (Query 1 sec) SELECT count(*) 
    • printを付ければkillされません.--execute-commandでコマンドを実行させたり.--wait-before- kill,--wait-after-killでkillする前や後にコマンドを実行させたりも可能.--kill-queryでマッチしたクエリ,--killだけだとマッチしたコネクション全部になります.--printにしていてももKILLという文字列が入るだけで怖くなります.
    • match-command SleepとかConnectとすると指定コマンドを,--match-db,--match-host,--match-info(クエリ),--match-userなどをperl正規表現でマッチしたものだけを殺したり表示したりすることも可能.

サービスと関係のない指定外のサーバからの重いクエリは排除するなど,便利に使えそうです.

mk-loadavg

mysqlの負荷を見て、指定したスレッド数やloadavgやスワップなどの基準を超えたら、指定したコマンドを実行するツール.

[hxmasaki@hmsk maatkit] # mk-loadavg -h192.168.10.210 --watch "Processlist:command:Query:time:>:5" --execute-command query-heavy.sh&

こんな感じで実行しておくと,processlist中でTime 5を超えたクエリが発生した場合にquery-heavy.shが実行される.
アラートを上げさせたりロードバランサーから自分への割り振りを減らしたりなど出来そうです.

mk-logplayer

まだ試しておりません.

mk-parallel-dump

並列処理でテーブルダンプしてくれるツール

[root@db-master ~]# mk-parallel-dump -D service --no-gzip

chunksizeやスレッド数の設定等も出来ます.ただ,実行中すごく重くて対象サーバとコマンド実行サーバでは何も出来ない状態になってしまいます.110GBのDBに対して実行したら,普通にdumpにかかる時間と比較するどころじゃなく,半日待たされても終わらなかったため,中断してしまいました.
再度軽いものを対象に試してみます.

mk-parallel-restore

mk-parallel-dumpの逆のツール.
まだ試しておりません.

mk-profile-compact

mk-query-profilerの出力を整形するツール.
まだ試しておりません.

mk-query-profiler

実行したいクエリのファイルを与えるとそれによる負荷や経過時間などを算出する
まだ試しておりません.

mk-query-digest

slowlogを集計して負荷、時間、対象行などをレポートするツール.
解析をかけたい分のslowlogを入力として,解析結果を出力してくれる.

[hxmasaki@hmsk maatkit] # mk-query-digest db-slow.log --report-all

といった感じで実行すると,結果を出力するので,ファイルにリダイレクトするなりなんなりする.
出力結果は以下のようになっている(一部省略).出力の上部は各クエリごとの詳細,下部は全クエリのレスポンスタイムの合計値で並べられたランキングになっている.

# Query 73: 0.00 QPS, 0.00x concurrency, ID 0x2256C8F4C0666085 at byte 322977024
# This item is included in the report because it matches --outliers.
#              pct   total     min     max     avg     95%  stddev  median
# Count          0      10
# Exec time      0     20s      2s      2s      2s      2s       0      2s
# Lock time      0       0       0       0       0       0       0       0
# Rows sent      0       6       0       1    0.60    0.99    0.49    0.99
# Rows exam      0       6       0       1    0.60    0.99    0.49    0.99
# Users                  1 write_user
# Hosts                  1
# Databases              1    service
# Time range 2010-04-21 07:05:39 to 2010-05-10 05:58:15
# bytes          0     633      56      77   63.30   69.19    6.19   59.77
# Query_time distribution
#   1us
#  10us
# 100us
#   1ms
#  10ms
# 100ms
#    1s  ################################################################
#  10s+
# Tables
#    SHOW TABLE STATUS FROM `service` LIKE 'comments'\G
#    SHOW CREATE TABLE `service`.`comments`\G
# EXPLAIN
SELECT * FROM `comments` WHERE (subject = '無題')  LIMIT 1\G

# Profile
# Rank Query ID           Response time     Calls R/Call   Item
# ==== ================== ================= ===== ======== ===============
#    1 0x1AD058F6B2B1C729 283809.0000 39.3% 12707  22.3349 SELECT service.daily_posts service.comments
#    2 0x406C0D65D82E1378  85750.0000 11.9% 37685   2.2754 INSERT daily_posts
#    3 0x54F9616817F34E26  83601.0000 11.6% 10300   8.1166 SELECT trackbacks refers
#    4 0xA0E155F694763885  77228.0000 10.7% 34552   2.2351 INSERT daily_posts

この集計からサービスのコード中で発行しているクエリの改善等を行っています.便利.

mk-show-grants

grantの情報をサマリーしてくれるツール.

[hxmasaki@hmsk maatkit]$ mk-show-grants -h192.168.10.210 -uroot -pXXXXXX
-- Grants dumped by mk-show-grants 1.0.22
-- Dumped from server 192.168.10.210 via TCP/IP, MySQL 5.0.51-modified-log at 2010-05-25 19:13:44
-- Grants for 'read'@'localhost'
GRANT USAGE ON *.SELECT TO 'read'@'localhost';
-- Grants for 'write'@'192.168.%'
GRANT SELECT ON *.UPDATE,INSERT TO 'write'@'192.168.%' IDENTIFIED BY PASSWORD 'fe84f50e336e9703';
    • revokeを入れるとREVOKEも一緒に,加えて--separateを入れると,GRANT,REVOKEを分けて出力してくれる.

新しくdb増やす時はlvmで丸ごと持って行ったり,MyISAMmysqlデータベースのファイルをコピーするだけで,grantの再設定をする機会はあまりなかったりしますが,他のサーバとデータが一致しているかとか,実際使われているgrantの情報をまとめる時などに使っている気がします.

mk-slave-delay

レプリケーションのslaveをmasterから意図的,段階的に遅延を起こす.
これだ!という使いどころが僕には思いつきませんでした.動作もちゃんと追ってないのですが,遅らせることでmasterもしくはslaveのIOが止まるのであれば使いどころがありそうな気がします.
今のところまだ試しておりません.

mk-slave-find

masterをレプリケーションしているslaveヒエラルキーをツリーで出力する.

[hxmasaki@hmsk maatkit]$ mk-slave-find -h192.168.10.210 -uroot -p 'XXXXX'
192.168.10.210
+- 192.168.10.209
   +- 192.168.10.211
[hxmasaki@hmsk maatkit]$ mk-slave-find -h192.168.10.209 -uroot -p 'XXXXX'
192.168.10.209
+- 192.168.10.211
+- 192.168.10.210

この出力は192.168.10.210と209のデュアルマスタ構成で209にスレーブとして211をぶらさげている状態です.
どうもプロセスリストから出力しているみたいで,次に出て来るmk-slave-moveなどでぶら下げ変えを行っても,sleepなコネクションとしてリストに残るせいでリアルタイムな情報が出力されません.

mk-slave-move

レプリケーションの親を変える.
レプリケーションが三世代(親-->子-->孫)のときに,孫を子の兄弟に昇格させたり,その状態から戻したり出来る.

上のmk-slave-findの例のヒエラルキー上で

[hxmasaki@hmsk maatkit]$ mk-slave-move 192.168.10.211 --sibling-of-master

とすることで,10.209にぶらさがっていた10.211を,10.210にぶらさげ変えることが出来る.デュアルマスタだと親とか子とか兄弟とかで考えると頭がおかしくなれます.*1
実行時にぶらさげ先と,ぶら下げ変える対象のホストはレプリケーションが止まるので注意.

mk-slave-prefetch

リレーログをslaveに流しこんで事前にキャッシュを温められる.
MyISAM の場合,重そうなテーブルのデータをsysreadなどしてメモリ上にデータを乗せておくなんてことが出来ますが,Innodbなど,それが簡単に出来ない場合は実際にクエリを実行する必要があるため,そういった時に役立ちそうです.
まだ試しておりません.

mk-slave-restart

レプリケーションエラーを監視して,特定のエラーコードに該当したら,そのエラーをスキップしてレプリケーションを再開してくれる.

ずっとslave statusを監視して,1317が出たら,次のポジションへスキップしてくれる.人が毎度エラーを見て,スキップして,slaveを再開するという作業が完全自動化される!デーモナイズも可能.

[hxmasaki@hmsk ~]$  mk-slave-restart -hXXXXX -uXXXX -pXXXXX--error-numbers 1317 --verbose
2010-06-10T15:34:22 S=/var/lib/mysql/mysql.sock,h=192.168.10.120,p=...,u=root relay-bin.000869   213471348 1317 Query partially completed on the master (error on master: 1317) and was aborted. There is a chance that your master is inconsistent at this point. If you are sure that your master is ok, run this query manually on the slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE; . Query: 'DELETE FROM daily_posts WHERE id IN (22233)'
2010-06-10T15:34:26 S=/var/lib/mysql/mysql.sock,h=192.168.10.120,p=...,u=root relay-bin.000869   216778173 1317 Query partially completed on the master (error on master: 1317) and was aborted. There is a chance that your master is inconsistent at this point. If you are sure that your master is ok, run this query manually on the slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; START SLAVE; . Query: 'DELETE FROM daily_posts WHERE id IN (22233

間違ったクエリを流してしまいDuplicateしまくった時など,まず遭遇すべきでは無いのですが助かりました.
無限スキップやばい.

mk-table-checksum

レプリケーションの整合性チェックをする.
思った以上に重かったので途中で止めたまま,再度試しておりません.

mk-checksum-filter

mk-table-checksumの結果をソートしたり差分とったりする
上のデータを得られたら試してみようと思います.

mk-table-sync

テーブルの完全同期を行う.
テーブルの単純な同期だけでなく、全テーブルの同期、マスターを指定してスレーブのテーブルを同期、スレーブを指定してマスターのデータを同期なども行える.データをまるっと書き換えるので、実行前のバックアップを一応.

以下は,192.168.10.210のmaatkit.table_syncを192.168.10.211のmaatkit.table_syncに同期する例.

    • dry-runを指定すると,テーブルの比較まで実行される.
[hxmasaki@hmsk maatkit]$ mk-table-sync --dry-run h=192.168.10.210,D=maatkit,t=table_sync 192.168.10.211
# NOTE: --dry-run does not show if data needs to be synced because it
#       does not access, compare or sync data.  --dry-run only shows
#       the work that would be done.
# Syncing D=maatkit,S=/var/lib/mysql/mysql.sock,h=192.168.10.210,p=...,t=table_sync,u=root in dry-run mode, without accessing or comparing data
# DELETE REPLACE INSERT UPDATE ALGORITHM EXIT DATABASE.TABLE
#      0       0      0      0 Chunk     0    maatkit.table_sync
    • printを指定すると,同期するために実行されるクエリを確認できる.実際に実行はされない.
[hxmasaki@hmsk maatkit]$ mk-table-sync --print h=192.168.10.210,D=maatkit,t=table_sync 192.168.10.211
INSERT INTO `maatkit`.`table_sync`(`id`, `title`) VALUES (2, 'a');
    • executeを指定すると,実行される.
[hxmasaki@hmsk maatkit]$ mk-table-sync --execut h=192.168.10.210,D=maatkit,t=table_sync 192.168.10.211

同期元が824685件,同期先が0件のテーブル同期に24分かかった.素直にdumpしてimportした方が早いかもしれない。
実行例をid:satoshipさんに頂きました.ありがとうございました.

mk-upgrade

複数のサーバへクエリを送って、時間やエラー、結果などの違いを比較する(mysqlをアップグレードする前に試したいから、このコマンド名称?)
まだ試しておりません.

mk-visual-explain

EXPLAINの結果を入力すると,クエリの構成をツリー構造で表現してくれます.
以下実行例です.EXPLAIN SELECT ~なクエリの実行結果をsample_explain.logとして保存してあるものをmk-visual-explainに投げています.

[hxmasaki@hmsk maatkit] # mk-visual-explain sample_explain.log
JOIN+- Filter with WHERE
|  +- Bookmark lookup
|     +- Table
|     |  table          comments|     |  possible_keys  ID_ViewMode,AcceptDate
|     +- Index lookup
|        key            comments->ID_ViewMode
|        possible_keys  ID_ViewMode,AcceptDate
|        key_len        5
|        ref            service.comments.id|        rows           9
+- Filter with WHERE
   +- Bookmark lookup
      +- Table
      |  table          comments
      |  possible_keys  PRIMARY,index_comments_on_user_id,index_comments_on_published_at
      +- Index lookup
         key            comments->index_comments_on_user_id
         possible_keys  PRIMARY,index_comments_on_user_id,index_comments_on_published_at
         key_len        5
         ref            const
         rows           105