Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ぼくがかんがえたさいきょうのMySQLの監視スクリプトを読み解く

yoku0825
March 28, 2021

 ぼくがかんがえたさいきょうのMySQLの監視スクリプトを読み解く

yoku0825

March 28, 2021
Tweet

More Decks by yoku0825

Other Decks in Technology

Transcript

  1. \こんにちは/ yoku0825@とある企業のDBA オラクれない ‐ ポスグれない ‐ マイエスキューエる ‐ 生息域 Twitter:

    @yoku0825 ‐ Blog: 日々の覚書 ‐ 日本MySQLユーザ会 ‐ MySQL Casual ‐ 今日13:10~のTrack Bのセッションにもちょっとだけゲスト出演しています トラブルのない決済システムを作ろうと奮闘したお話 by 千葉 ‐ 7/59
  2. yt-healthcheck ytkit の1スクリプト ytkit is Y oku-san no T ool

    kit ‐ 他にも yt-binlog-groupby, yt-alter-progress, yt-wait-replication あたりがべ んり 11/59
  3. ロールの自動識別 デフォルトではmaster, slave, intermidiate(中間マスター), group_replication を 自動判別 する 自動ったって大したことをしているわけではなくて、 ‐

    胸に手を当てて思い出してみよう。DBAは知らないMySQLをポンと与えられた時に、それがマ スターかレプリカかどうやって判別している? ‐ 16/59
  4. ロールの自動識別 my $master= my $slave= my $cluster= 0; $master= 1

    if $self->show_slaves_via_processlist; $slave = 1 if ($self->instance->show_slave_status && $self->instance->show_slave_status->[0]); $cluster= 1 if defined($self->instance->i_am_group_replication_primary); ### Restore param $self->instance->{_ignore_unsupport_version}= $saved_ignore; ### SHOW SLAVE STATUS condition is advanced more than cluster. if ($slave) { ### Intermidiate-master in a cascaded replication toporogy. return "intermidiate" if $master; return "slave"; } elsif ($cluster) { return "group_replication"; } else { return "master"; } https://github.com/yoku0825/ytkit/blob/0.5.0/lib/Ytkit/ HealthCheck.pm#L217-L249 17/59
  5. ロールの自動識別 1. SHOW PROCESSLIST を叩く Commandが Binlog Dump か Binlog

    GTID Dump のやつがいれば、それはたぶんレプリカがつな ぎにきている = コイツはレプリケーションソース ‐ mysql> SHOW PROCESSLIST; +-----+-----------------+-----------------+------+-------------+------+------------------ ---------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+-----------------+-----------------+------+-------------+------+------------------ ---------------------------------------------+------------------+ | 5 | event_scheduler | localhost | NULL | Daemon | 38 | Waiting on empty queue | NULL | | 10 | rsandbox | localhost:37548 | NULL | Binlog Dump | 17 | Master has sent a ll binlog to slave; waiting for more updates | NULL | | 12 | msandbox | localhost | NULL | Query | 0 | init | SHOW PROCESSLIST | +-----+-----------------+-----------------+------+-------------+------+------------------ ---------------------------------------------+------------------+ 18/59
  6. ロールの自動識別 2. SHOW SLAVE STATUS を叩く ソースかレプリカかの判定なので、今は中身は気にしない ‐ 「結果セットが空 =

    レプリカではない」か、「1行以上存在している = レプリカである」かの どちらか。 ‐ mysql> SHOW SLAVE STATUS\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 127.0.0.1 Master_User: rsandbox Master_Port: 23631 ... 19/59
  7. ロールの自動識別 3. performance_schema.replication_group_members を叩く これも今は中身は気にしない ‐ 「結果セットが空 = グループレプリケーションのメンバーではない」か、「1行以上存在して いる

    = グループレプリケーションを組んでいる」かのどちらか ‐ 割と最近組み込んだ機能 ‐ mysql> SELECT * FROM performance_schema.replication_group_members; +---------------------------+--------------------------------------+--------------------- ---------+-------------+--------------+-------------+----------------+ | CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +---------------------------+--------------------------------------+--------------------- ---------+-------------+--------------+-------------+----------------+ | group_replication_applier | f80bad73-79a0-11eb-afa3-12053f89f52d | ip-172-31-1-141.ec2. internal | 3306 | ONLINE | PRIMARY | 8.0.23 | +---------------------------+--------------------------------------+--------------------- ---------+-------------+--------------+-------------+----------------+ 20/59
  8. ロールの自動識別 4. 1. ~ 3. の辻褄を合わせる 現状、グループレプリケーションを非同期レプリカにするトポロジーはサポートしていない( -- role=group_replication と

    --role=slave で2回実行すればいいだけだと思う) ‐ role $master $slave $cluster master o x * slave x o * intermidiate o o * group_replication x x o 21/59
  9. ロングクエリー監視 「mysqldとしては生きているけどクエリーの結果がなかなか返ってこない」を判 定するための機構 サチってる時はわりと綺麗にこれが一番最初に鳴ってくれるようになった ‐ バッチ用のホスト/アカウント/クエリー(先頭マッチ)は除外するオプションを用意 している オオカミ少年 (・A・)イクナイ!! ‐

    --long_query_exclude_host, --long_query_exclude_user, --long_query_exclude_query ‐ 長いクエリーでも「1本だけならいいや、本当にサチった時は2本以上引っ掛かるは ずだし」という閾値も設定可能 これ普段2(1本だけなら良い、2本以上ならアラート)にしているけれどこっちをデフォルトにし ようかってくらいノイズが減った ‐ --long_query_min_critical_thread, --long_query_min_warning_thread ‐ 23/59
  10. ロングクエリー監視 やってることは SHOW FULL PROCESSLIST の Time を見ているだけ 明らかにevent_schedulerとかBinlog Dumpとかは除外している

    ‐ +-----+-----------------+-----------+-----------+---------+------+----------------------------+------------ ------------------------------------------------------------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+-----------------+-----------+-----------+---------+------+----------------------------+------------ ------------------------------------------------------------------------------------------+ | 5 | event_scheduler | localhost | NULL | Daemon | 44 | Waiting on empty queue | NULL | | 21 | root | localhost | mysqlslap | Query | 0 | waiting for handler commit | INSERT INTO t1 VALUES (73673339,'BN3152Gza4GW7atxJKACYwJqDbFynLxqc0kh30YTwgz3FktQ43XTrqJ4PQ25frn7kXh | | 22 | root | localhost | NULL | Query | 0 | init | show proces slist | +-----+-----------------+-----------+-----------+---------+------+----------------------------+------------ ------------------------------------------------------------------------------------------+ https://github.com/yoku0825/ytkit/blob/0.5.0/lib/Ytkit/ HealthCheck.pm#L278-L319 24/59
  11. コネクション数監視 mysql> SHOW GLOBAL STATUS LIKE 'Threads_connected'; +-------------------+-------+ | Variable_name

    | Value | +-------------------+-------+ | Threads_connected | 3 | +-------------------+-------+ 1 row in set (0.03 sec) mysql> SHOW GLOBAL VARIABLES LIKE 'max_connections'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | max_connections | 151 | +-----------------+-------+ 1 row in set (0.00 sec) 26/59
  12. レプリケーション状況監視 Slave_*_Running がYes(=レプリケーションが止まっていない)か Seconds_Behind_Master が閾値未満(=レプリケーションが遅れていない)か mysql> SHOW SLAVE STATUS\G ***************************

    1. row *************************** Slave_IO_State: Waiting for master to send event .. Slave_IO_Running: Yes Slave_SQL_Running: Yes .. Seconds_Behind_Master: 0 .. https://github.com/yoku0825/ytkit/blob/0.5.0/lib/Ytkit/ HealthCheck.pm#L438-L474 27/59
  13. AUTO_INCREMENT監視 AUTO_INCREMENTなカラムがデータ型の限界値に近づいていないかのチェック AUTO_INCREMENTの現在値は information_schema.tables から引ける AUTO_INCREMENT属性のカラム名とデータ型は information_schema.columns か ら引ける AUTO_INCREMENTの値をデータ型の限界値(UNSIGNED

    INTなら 2^32 -1 とか) で割った値でアラートを出し分ける my ($type, $unsigned)= $row->{column_type} =~ /^([a-z]+)(?:\(.+\))?(\s+(unsigned))?/; my $max= 2 ** (BYTES->{$type} * 8 - ($unsigned ? 0 : 1)); my $ratio= ($row->{auto_increment} / $max) * 100; https://github.com/yoku0825/ytkit/blob/0.5.0/lib/Ytkit/ HealthCheck.pm#L368-L413 28/59
  14. AUTO_INCREMENT監視 mysql> SELECT -> table_schema AS table_schema, -> table_name AS

    table_name, -> column_name AS column_name, -> auto_increment AS auto_increment, -> column_type AS column_type -> FROM -> information_schema.tables -> JOIN information_schema.columns USING(table_schema, table_name) -> WHERE -> table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys') AND -> table_type = 'BASE TABLE' AND -> auto_increment IS NOT NULL AND -> extra = 'auto_increment'; +--------------+------------+-------------+----------------+-------------+ | table_schema | table_name | column_name | auto_increment | column_type | +--------------+------------+-------------+----------------+-------------+ | sbtest | sbtest1 | id | 10001 | int | | sbtest | sbtest2 | id | 10001 | int | | sbtest | sbtest3 | id | 10001 | int | | sbtest | sbtest4 | id | 10001 | int | | sbtest | sbtest5 | id | 10001 | int | | sbtest | sbtest6 | id | 10001 | int | | sbtest | sbtest7 | id | 10001 | int | | sbtest | sbtest8 | id | 10001 | int | | sbtest | sbtest9 | id | 10001 | int | | sbtest | sbtest10 | id | 10001 | int | +--------------+------------+-------------+----------------+-------------+ 10 rows in set (0.01 sec) 29/59
  15. read_only設定監視 そのまま。 read_only が意図した値になっているかどうか確認する。 role read_only=ON read_only=OFF master 大事故 o

    slave o 接続先を間違えなければ… intermidiate 大事故 o なんでこんな単純な監視が存在するのかと言うと、「それで痛い目を見たことがあ るから」 31/59
  16. デッドロック発生時間監視 飽くまで「Nagiosライク」に単発でチェックするので、現在時刻とデッドロック発 生時刻の差だけをチェック ウチでは5分間隔のポーリングなので「5分以内ならワーニング」とか ‐ 正直あんまり需要はないと思う ‐ ===================================== 190708 11:49:25

    INNODB MONITOR OUTPUT ===================================== Per second averages calculated from the last 18 seconds ---------- SEMAPHORES ---------- OS WAIT ARRAY INFO: reservation count 201960, signal count 160833 Mutex spin waits 0, rounds 24340763, OS waits 81459 RW-shared spins 152853, OS waits 73723; RW-excl spins 54434, OS waits 30136 ------------------------ LATEST DETECTED DEADLOCK ------------------------ 190708 11:11:59 <-- ココ *** (1) TRANSACTION: TRANSACTION 0 29557, ACTIVE 1 sec, process no 4356, OS thread id 140577570899712 inserting mysql tables in use 1, locked 1 LOCK WAIT 11 lock struct(s), heap size 1216, undo log entries 4 32/59
  17. デッドロック発生時間監視 自慢(?)したいことはただ1つだけ この機能をテストするためだけに、それぞれ少しずつ出力が違うMySQL 5.0, 5.1, 5.5, 5.6, 5.7, 8.0の SHOW

    ENGINE INNODB STATUS のテストデータがある https://github.com/yoku0825/ytkit/blob/0.5.0/t/data/show_engine_innodb_status.pl ‐ 33/59
  18. history_list_length監視 現在溜まっているUNDOログページの総数(長時間継続しているトランザクション があるか、UNDOログのパージが更新に追い付いていないか) information_schema.innodb_metrics (5.6とそれ以降) がなかった時代は SHOW ENGINE INNODB STATUS

    の中に history_list_length という表示で含まれていた ‐ 今日日 SHOW ENGINE INNODB STATUS をパースしなくても取り出せる ‐ mysql> SELECT name, count FROM information_schema.innodb_metrics WHERE name = 'trx_rseg_h istory_len'; +----------------------+-------+ | name | count | +----------------------+-------+ | trx_rseg_history_len | 69 | +----------------------+-------+ 1 row in set (0.00 sec) 35/59
  19. グループレプリケーション状況監視 MySQL InnoDB Clusterが利用している「グループレプリケーション」は今までの レプリケーションと監視方法が明らかに違う たとえば SHOW SLAVE STATUS は空っぽ

    ‐ performance_schemaを使った監視をしようとしても、グループレプリケーションは replication_replication_member_stats あたり、非同期レプリケーションは replication_applier_status_by_worker あたり ‐ performance_schemaを使っていても、カラムの名前とかまだ人口に膾炙しているというほど ではない ‐ mysql> SHOW SLAVE STATUS; Empty set, 1 warning (0.00 sec) mysql> SELECT * FROM performance_schema.replication_group_members; +---------------------------+--------------------------------------+------------------------------+-------------+--------------+-------------+----------------+ | CHANNEL_NAME | MEMBER_ID | MEMBER_HOST | MEMBER_PORT | MEMBER_STATE | MEMBER_ROLE | MEMBER_VERSION | +---------------------------+--------------------------------------+------------------------------+-------------+--------------+-------------+----------------+ | group_replication_applier | 39629f0d-8243-11eb-964e-12bfe5404f29 | ip-172-31-1-192.ec2.internal | 3306 | ONLINE | SECONDARY | 8.0.23 | | group_replication_applier | 3a80d620-8243-11eb-a852-12b379095b1f | ip-172-31-1-103.ec2.internal | 3306 | ONLINE | PRIMARY | 8.0.23 | | group_replication_applier | 3a999d81-8243-11eb-a8f0-12e72ffe935b | ip-172-31-1-73.ec2.internal | 3306 | ONLINE | SECONDARY | 8.0.23 | +---------------------------+--------------------------------------+------------------------------+-------------+--------------+-------------+----------------+ 3 rows in set (0.00 sec) 37/59
  20. グループレプリケーション状況監視 グループレプリケーションの「遅延」を監視 SHOW SLAVE STATUS の Seconds_Behind_Master のようなシンプルな値ではなく(アレは実は 「算出方法」がシンプルではないけれど)、自分で(SQLで)計算している ‐

    「適用待ちトランザクションキューの数」と「最後にコミットされたトランザクションの時間 差」を計算 ‐ ### How many commits not yet applied. my $trx_lag= $self->instance->replication_group_member_stats->[0]->{count_transactions_re mote_in_applier_queue}; ### How many seconds between "STARTING APPLY" and "COMMITTED ORIGINAL" my $applier_time_lag= $self->instance->replication_applier_status->{group_replication_app lier}->{_diff} // 0; https://github.com/yoku0825/ytkit/blob/0.5.0/lib/Ytkit/ HealthCheck.pm#L773-L804 39/59
  21. グループレプリケーション状況監視 遅延監視ではグループレプリケーションの RECOVERING ステータス(一度グループレ プリケーションから弾き出されたあと、差分同期でグループレプリケーションに追 い付くまでのステータス)も考慮 落ちて上がったあと、延々鳴り続けるのも嫌ですしおすし ‐ ### If

    node is entering RECOVERING state, mysqlrouter is devide node from load-balancing, ### Group Replication lag is not matter. if ($self->instance->i_am_group_replication_recovering) { ### Change critical threshold to supernum to fall back as WARNING $self->{group_replication_lag_transactions}->{critical}= 2 ** 63 - 1; $self->{group_replication_lag_seconds}->{critical}= 2 ** 63 - 1; } https://github.com/yoku0825/ytkit/blob/0.5.0/lib/Ytkit/ HealthCheck.pm#L773-L804 40/59
  22. 監視の閾値調整 $ yt-healthcheck --help | grep Default * --autoinc_usage_critical=value {

    Default: 90 } * --autoinc_usage_enable=value { Default: 1 } * --autoinc_usage_warning=value { Default: 50 } * --connection_count_critical=value { Default: 95 } * --connection_count_enable=value { Default: 1 } * --connection_count_warning=value { Default: 70 } * --deadlock_critical=value { Default: 1 } * --deadlock_enable=value { Default: 0 } * --deadlock_warning=value { Default: 300 } .. 43/59
  23. 後追いできるように情報を残す --help にあるよりもっと情報を取ってた SHOW FULL PROCESSLIST ‐ SHOW SLAVE STATUS

    ‐ SHOW ENGINE INNODB STATUS ‐ sys.x$innodb_lock_waits (sysがないバージョンでは似たようなSQLで直接 information_schemaを叩く) ‐ information_schema.innodb_trx ‐ performance_schema.threads ‐ https://github.com/yoku0825/ytkit/blob/0.5.0/lib/Ytkit/ MySQLServer.pm#L1138-L1212 46/59
  24. テスト ロジックに間違いがあると、通知してほしいアラートが通知されない… のは良い としても (PerlでもSQLでも)シンタックスエラーなんぞ入れ込もうもんなら、監視している 全台から一斉にアラートが上がる(切実) 迂闊に非互換な変更を入れるとデプロイのタイミングのズレで yt-healthcheck を 使っている監視スクリプトが一斉にエラーを吐く(切実)

    ---------------------------- ------ ------ ------ ------ ------ ------ ------ File stmt bran cond sub pod time total ---------------------------- ------ ------ ------ ------ ------ ------ ------ lib/Ytkit/HealthCheck.pm 77.9 66.3 60.0 97.4 0.0 0.0 72.2 lib/Ytkit/MySQLServer.pm 96.7 83.0 70.3 98.8 0.0 14.5 86.0 49/59
  25. ライセンス GPLv2 or later コピーレフトが良いかどうかは好みだとして、要は「オープンソース」だというこ と とても原始的なオープンソースと企業のエコシステム 会社 は 従業員

    に オープンソース開発への参加を業務時間で(職務著作とせず)許可 ‐ 会社 は オープンソース成果物 を利用 ‐ 従業員=コミッター は優先的に 会社 の要望を開発する ‐ 51/59
  26. オープンソースな監視スクリプト 監視のロジックを公開できる 人に説明する時にべんり ‐ SQLも読める ‐ 自分に縁のないプログラミング言語でも、大体雰囲気はつかめるはず ましてや変数の頭に $ ですよ!

    ‐ コミットログの積み上げの良い練習になる いやーrebaseしすぎると意図を忘れますね ‐ stashしまくるとつらい ‐ Issue ⇒ テストケースを追加したブランチでPull-Req ⇒ そのブランチにFix が好みかな ‐ とかとかとか ‐ 52/59
  27. yt-healthcheck ytkitのリポジトリはこちら! yoku0825/ytkit: Yoku\-san no Tool KIT ‐ Issues/Pull-Reqs are

    welcome :D MySQLの状態確認の参考実装としても使えます ‐ 55/59