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

バッファープールが大きいMySQL v5.7でDROP DATABASEが詰まった原因と対策 / Causes and Remedies for DROP DATABASE Stuck in MySQL v5.7 with Large Buffer Pool

バッファープールが大きいMySQL v5.7でDROP DATABASEが詰まった原因と対策 / Causes and Remedies for DROP DATABASE Stuck in MySQL v5.7 with Large Buffer Pool

Otsuka Tomoaki / LINE株式会社 ITサービスセンター Database室 MySQL1チーム

バッファプールが100GB以上あるMySQL 5.7でDROP TABLEを実行すると準同期レプリケーションとの兼ね合いでロックが発生し、予期しないフェイルオーバが発生する事態になりました。 今回はこの原因と対策を紹介します。なお、MySQLの最新メジャーバージョンである8.0では解決しています。

イベントページ
https://line.connpass.com/event/255090/

LINE Developers
PRO

August 02, 2022
Tweet

More Decks by LINE Developers

Other Decks in Technology

Transcript

  1. MySQL 5.7でDROP DATABASEが
    詰まった原因の調査過程と対策
    LINE, ITSC Database department
    MySQL1 team, ⼤塚知亮(@tom__bo)
    1

    View Slide

  2. ⾃⼰紹介

    所属: LINE ITSC, DB室 MySQL 1チーム

    業務内容:
    • MySQLの運⽤・⾃動化
    • MySQL関連の技術調査(TiDB, Vitess, etc...)

    Blog: https://tombo2.hatenablog.com/

    Twitter: @tom__bo
    2

    View Slide

  3. 今回発表する内容

    DELETE⽂と別のスキーマに対するDROP DATABASE⽂の実⾏
    でMySQLがハングしてメンテ失敗になった事例紹介

    障害後の原因調査・対策作りまでの流れ
    • MySQL 5.7
    • 準同期レプリケーション
    • Innodb_buffer_pool_sizeが⼤きい
    • 削除対象のテーブルが⼤きい
    3

    View Slide

  4. 問題に⾄るまでの経緯
    4

    View Slide

  5. シャード分割概要

    シャード数を2倍にする作業をメンテありで実施

    既存シャードそれぞれから2つの新シャードにレプリケーション

    メンテ中に半分のデータを削除・アプリの向き先変更
    5
    Old shard
    New shard1
    New shard2
    data data
    data
    data
    data
    data

    View Slide

  6. シャード分割概要

    シャード数を2倍にする作業をメンテありで実施

    既存シャードそれぞれから2つの新シャードにレプリケーション

    メンテ中に半分のデータを削除・アプリの向き先変更
    6
    Old shard
    New shard1
    New shard2
    data data
    data
    data
    data
    data
    前半部分
    後半部分

    View Slide

  7. 分割作業
    • メンテ後、新シャードで新しいスキーマを作成して必要なデータを抽出
    or テーブルを移動して不要なデータを消し込み
    7
    OLD
    OLD
    OLD
    -- 新規、移⾏先スキーマ・テーブル作成
    CREATE DATABASE NEW;
    CREATE TABLE NEW.{not_big_tables} like OLD.{not_big_tables};
    -- データ量が少ないテーブルの抽出
    INSERT INTO NEW.{not_big_tables} SELECT * from OLD. {not_big_tables} where data_match_to(new_shardN);
    -- データ量が多いテーブルの移動
    RENAME TABLE OLD.{big_tables} to NEW.{big_tables}
    -- データ量が多いテーブルの消込み (Lazy Delete)
    LOOP:
    DELETE FROM NEW.{big_tables} where data_not_match_to(new_shardN) LIMIT 1000;
    -- 古いスキーマの削除
    DROP DATABASE OLD;
    Old shard
    NEW shard1
    NEW shard2

    View Slide

  8. 分割作業
    • メンテ後、新シャードで新しいスキーマを作成して必要なデータを抽出
    or テーブルを移動して不要なデータを消し込み
    8
    OLD
    OLD
    OLD
    NEW
    NEW
    -- 新規、移⾏先スキーマ・テーブル作成
    CREATE DATABASE NEW;
    CREATE TABLE NEW.{not_big_tables} like OLD.{not_big_tables};
    -- データ量が少ないテーブルの抽出
    INSERT INTO NEW.{not_big_tables} SELECT * from OLD. {not_big_tables} where data_match_to(new_shardN);
    -- データ量が多いテーブルの移動
    RENAME TABLE OLD.{big_tables} to NEW.{big_tables}
    -- データ量が多いテーブルの消込み (Lazy Delete)
    LOOP:
    DELETE FROM NEW.{big_tables} where data_not_match_to(new_shardN) LIMIT 1000;
    -- 古いスキーマの削除
    DROP DATABASE OLD; CREATE DATABSE…
    CREATE TABLE…
    Old shard
    NEW shard1
    NEW shard2

    View Slide

  9. 分割作業
    • メンテ後、新シャードで新しいスキーマを作成して必要なデータを抽出
    or テーブルを移動して不要なデータを消し込み
    9
    OLD
    OLD
    OLD
    NEW
    NEW
    -- 新規、移⾏先スキーマ・テーブル作成
    CREATE DATABASE NEW;
    CREATE TABLE NEW.{not_big_tables} like OLD.{not_big_tables};
    -- データ量が少ないテーブルの抽出
    INSERT INTO NEW.{not_big_tables} SELECT * from OLD. {not_big_tables} where data_match_to(new_shardN);
    -- データ量が多いテーブルの移動
    RENAME TABLE OLD.{big_tables} to NEW.{big_tables}
    -- データ量が多いテーブルの消込み (Lazy Delete)
    LOOP:
    DELETE FROM NEW.{big_tables} where data_not_match_to(new_shardN) LIMIT 1000;
    -- 古いスキーマの削除
    DROP DATABASE OLD;
    INSERT INTO … SELECT
    Old shard
    NEW shard1
    NEW shard2

    View Slide

  10. 分割作業
    • メンテ後、新シャードで新しいスキーマを作成して必要なデータを抽出
    or テーブルを移動して不要なデータを消し込み
    10
    OLD
    OLD
    OLD
    NEW
    NEW
    -- 新規、移⾏先スキーマ・テーブル作成
    CREATE DATABASE NEW;
    CREATE TABLE NEW.{not_big_tables} like OLD.{not_big_tables};
    -- データ量が少ないテーブルの抽出
    INSERT INTO NEW.{not_big_tables} SELECT * from OLD. {not_big_tables} where data_match_to(new_shardN);
    -- データ量が多いテーブルの移動
    RENAME TABLE OLD.{big_tables} to NEW.{big_tables}
    -- データ量が多いテーブルの消込み (Lazy Delete)
    LOOP:
    DELETE FROM NEW.{big_tables} where data_not_match_to(new_shardN) LIMIT 1000;
    -- 古いスキーマの削除
    DROP DATABASE OLD; RENAME TABLE OLD to
    NEW
    Old shard
    NEW shard1
    NEW shard2

    View Slide

  11. 分割作業
    • メンテ後、新シャードで新しいスキーマを作成して必要なデータを抽出
    or テーブルを移動して不要なデータを消し込み
    11
    OLD
    OLD
    OLD
    NEW
    NEW
    -- 新規、移⾏先スキーマ・テーブル作成
    CREATE DATABASE NEW;
    CREATE TABLE NEW.{not_big_tables} like OLD.{not_big_tables};
    -- データ量が少ないテーブルの抽出
    INSERT INTO NEW.{not_big_tables} SELECT * from OLD. {not_big_tables} where data_match_to(new_shardN);
    -- データ量が多いテーブルの移動
    RENAME TABLE OLD.{big_tables} to NEW.{big_tables}
    -- データ量が多いテーブルの消込み (Lazy Delete)
    LOOP:
    DELETE FROM NEW.{big_tables} where data_not_match_to(new_shardN) LIMIT 1000;
    -- 古いスキーマの削除
    DROP DATABASE OLD;
    Loop: DELETE FROM NEW
    WHERE … LIMIT 1000
    (Lazy Delete)
    Old shard
    NEW shard1
    NEW shard2

    View Slide

  12. 分割作業
    • メンテ後、新シャードで新しいスキーマを作成して必要なデータを抽出
    or テーブルを移動して不要なデータを消し込み
    12
    OLD
    OLD
    OLD
    NEW
    NEW
    -- 新規、移⾏先スキーマ・テーブル作成
    CREATE DATABASE NEW;
    CREATE TABLE NEW.{not_big_tables} like OLD.{not_big_tables};
    -- データ量が少ないテーブルの抽出
    INSERT INTO NEW.{not_big_tables} SELECT * from OLD. {not_big_tables} where data_match_to(new_shardN);
    -- データ量が多いテーブルの移動
    RENAME TABLE OLD.{big_tables} to NEW.{big_tables}
    -- データ量が多いテーブルの消込み (Lazy Delete)
    LOOP:
    DELETE FROM NEW.{big_tables} where data_not_match_to(new_shardN) LIMIT 1000;
    -- 古いスキーマの削除
    DROP DATABASE OLD;
    Loop: DELETE FROM NEW
    WHERE … LIMIT 1000
    (Lazy Delete)
    DROP DATABASE OLD
    Old shard
    NEW shard1
    NEW shard2

    View Slide

  13. 分割作業
    • メンテ後、新シャードで新しいスキーマを作成して必要なデータを抽出
    or テーブルを移動して不要なデータを消し込み
    13
    OLD
    OLD
    OLD
    NEW
    NEW
    -- 新規、移⾏先スキーマ・テーブル作成
    CREATE DATABASE NEW;
    CREATE TABLE NEW.{not_big_tables} like OLD.{not_big_tables};
    -- データ量が少ないテーブルの抽出
    INSERT INTO NEW.{not_big_tables} SELECT * from OLD. {not_big_tables} where data_match_to(new_shardN);
    -- データ量が多いテーブルの移動
    RENAME TABLE OLD.{big_tables} to NEW.{big_tables}
    -- データ量が多いテーブルの消込み (Lazy Delete)
    LOOP:
    DELETE FROM NEW.{big_tables} where data_not_match_to(new_shardN) LIMIT 1000;
    -- 古いスキーマの削除
    DROP DATABASE OLD;
    Loop: DELETE FROM NEW
    WHERE … LIMIT 1000
    (Lazy Delete)
    DROP DATABASE OLD
    MySQL
    ハング!
    Old shard
    NEW shard1
    NEW shard2

    View Slide

  14. 再現実験
    14

    View Slide

  15. 調査内容

    なぜDROP DATABASEと関係ないスキーマへのDMLも詰まっ
    たのか?

    なぜDROP DATABASEに時間がかかったのか?
    • (省略、次回?)
    15

    View Slide

  16. 調査⽅法

    dstatコマンドによるOSメトリックの観測

    myStatusgoによるクエリ実⾏状況監視

    毎秒1⾏INSERTするスクリプト

    毎秒SHOW ENGINE INNODB STATUS

    詰まっている間のgstack `pidof mysqld`取得

    DROP DATABASE中のperf record, frameグラフ作成
    16

    View Slide

  17. 調査⽅法

    dstatコマンドによるOSメトリックの観測

    myStatusgoによるクエリ実⾏状況監視

    毎秒1⾏INSERTするスクリプト

    毎秒SHOW ENGINE INNODB STATUS

    詰まっている間のgstack `pidof mysqld`取得

    DROP DATABASE中のperf record, frameグラフ作成
    CPU, Disk IOに異常なし
    DROP DATABASE実⾏後、
    すべてのDMLが実⾏詰まった
    ことを確認
    後述
    (省略)
    17

    View Slide

  18. SHOW ENGINE INNODB STATUS

    SEMAPHORESセクションにミューテックス・セマフォ情報
    ----------
    SEMAPHORES
    ----------
    OS WAIT ARRAY INFO: reservation count 2469847
    --Thread 140577082132224 has waited at ha_innodb.cc line 15015 for 13 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    --Thread 140457661146880 has waited at row0purge.cc line 862 for 14 seconds the semaphore:
    S-lock on RW-latch at 0x7fc005af2858 created in file dict0dict.cc line 1183
    a writer (thread id 140577049569024) has reserved it in mode exclusive
    number of readers 0, waiters flag 1, lock_word: 0
    Last time read locked in file row0purge.cc line 862
    Last time write locked in file /PATH/TO/mysql-5.7.27/storage/innobase/row/row0mysql.cc line 5106
    --Thread 140577048487680 has waited at ha_innodb.cc line 5614 for 11 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    --Thread 140577083217664 has waited at ha_innodb.cc line 15015 for 13 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    --Thread 140577049028352 has waited at ha_innodb.cc line 15015 for 13 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    (...省略)
    (* /PATH/TOになっている部分は置換済み) 18

    View Slide

  19. SHOW ENGINE INNODB STATUS

    SEMAPHORESセクションにミューテックス・セマフォ情報
    ----------
    SEMAPHORES
    ----------
    OS WAIT ARRAY INFO: reservation count 2469847
    --Thread 140577082132224 has waited at ha_innodb.cc line 15015 for 13 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    --Thread 140457661146880 has waited at row0purge.cc line 862 for 14 seconds the semaphore:
    S-lock on RW-latch at 0x7fc005af2858 created in file dict0dict.cc line 1183
    a writer (thread id 140577049569024) has reserved it in mode exclusive
    number of readers 0, waiters flag 1, lock_word: 0
    Last time read locked in file row0purge.cc line 862
    Last time write locked in file /PATH/TO/mysql-5.7.27/storage/innobase/row/row0mysql.cc line 5106
    --Thread 140577048487680 has waited at ha_innodb.cc line 5614 for 11 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    --Thread 140577083217664 has waited at ha_innodb.cc line 15015 for 13 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    --Thread 140577049028352 has waited at ha_innodb.cc line 15015 for 13 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    (...省略)
    (* /PATH/TOになっている部分は置換済み) 19

    View Slide

  20. gstackでスレッドのスタック取得

    140577082132224 (0x7fdaa70b4700)を抜粋 (はみ出してます)
    Thread 9 (Thread 0x7fdaa70b4700 (LWP 3499)):
    #0 0x00007fdaa6c03a35 in [email protected]@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    #1 0x000000000107111b in wait (this=0x7fc005af28f8) at /PATH/TO/mysql-5.7.27/storage/innobase/os/os0event.cc:171
    #2 os_event::wait_low (this=0x7fc005af28f8, reset_sig_count=193) at /PATH/TO/mysql-5.7.27/storage/innobase/os/os0event.cc:358
    #3 0x0000000001118259 in sync_array_wait_event (arr=0x7fda986fa7f8, [email protected]: 0x7fda98006018) at /PATH/TO/mysql-5.7.27/storage/…
    #4 0x0000000001000d64 in TTASEventMutex::wait (this=0x7fdaa4b5a9d8, filename=0x1617ee0 "/PATH/TO/mysql-5.7.27/storage/inn…
    #5 0x0000000001000edb in spin_and_try_lock (line=15015, filename=0x1617ee0 "/PATH/TO/mysql-5.7.27/storage/innobase/handler/ha_innodb.cc", th…
    #6 enter (line=15015, filename=0x1617ee0 "/PATH/TO/mysql-5.7.27/storage/innobase/handler/ha_innodb.cc", max_delay=6, max_spins=30, this=0x7f…
    #7 PolicyMutex >::enter (this=0x7fdaa4b5a9d8, n_spins=30, n_delay=6, name=0x1617ee0 "/PATH/TO/mysql-5.7.27…
    #8 0x0000000000ff5aec in ha_innobase::get_foreign_key_list (this=0x7fbf3ac78030, thd=0x7fbf3c816000, f_key_list=0x7fdaa70b1e20) at /PATH/TO/my…
    #9 0x0000000000c841ff in has_cascade_foreign_key (table=, thd=) at /PATH/TO/mysql-5.7.27/sql/rpl_handler.cc:343
    #10 0x0000000000c84d80 in Trans_delegate::prepare_table_info (this=, thd=0x7fbf3c816000, [email protected]: 0x0, nu…
    #11 0x0000000000c86038 in Trans_delegate::before_dml (this=0x1e770a0 , thd=, [email protected]
    #12 0x0000000000cc0af1 in run_before_dml_hook (thd=) at /PATH/TO/mysql-5.7.27/sql/sql_base.cc:5334
    #13 0x0000000000e9aa4d in Sql_cmd_delete::mysql_delete (this=0x7fbf3c825fb8, thd=0x7fbf3c816000, limit=18446744073709551615) at /PATH/TO/m…
    #14 0x0000000000e9c17f in Sql_cmd_delete::execute (this=0x7fbf3c825fb8, thd=0x7fbf3c816000) at /PATH/TO/mysql-5.7.27/sql/sql_delete.cc:1393
    #15 0x0000000000d1c7cb in mysql_execute_command (thd=0x7fbf3c816000, first_level=true) at /PATH/TO/mysql-5.7.27/sql/sql_parse.cc:3606
    #16 0x0000000000d20cdd in mysql_parse (thd=0x7fbf3c816000, parser_state=) at /PATH/TO/mysql-5.7.27/sql/sql_parse.cc:5570
    #17 0x0000000000d21f05 in dispatch_command (thd=0x7fbf3c816000, com_data=0x7fdaa70b3da0, command=COM_QUERY) at /PATH/TO/mysql-5.7….
    #18 0x0000000000d22de4 in do_command (thd=0x7fbf3c816000) at /PATH/TO/mysql-5.7.27/sql/sql_parse.cc:1025
    #19 0x0000000000df4734 in handle_connection (arg=) at /PATH/TO/mysql-5.7.27/sql/conn_handler/connection_handler_per_thread.cc:306
    #20 0x0000000000f71ea4 in pfs_spawn_thread (arg=0x7fdaa481be20) at /PATH/TO/mysql-5.7.27/storage/perfschema/pfs.cc:2190
    #21 0x00007fdaa6bffea5 in start_thread () from /lib64/libpthread.so.0
    #22 0x00007fdaa56b7b0d in clone () from /lib64/libc.so.6
    (* /PATH/TOになっている部分は置換、⻑い⾏は…で省略) 20

    View Slide

  21. 実装調査
    21

    View Slide

  22. DROP DATABASEが取るDICT_SYS Mutex

    SEMAPHORESセクションにはMutexをとっているスレッドの
    情報は出⼒されない

    とりあえずSHOW ENGINE INNODB STATUSのSEMAPHORE
    セクションに出るMutexにbreakpointを張って処理内容を⾒る
    22
    ----------
    SEMAPHORES
    ----------
    OS WAIT ARRAY INFO: reservation count 2469847
    --Thread 140577082132224 has waited at ha_innodb.cc line 15015 for 13 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    --Thread 140457661146880 has waited at row0purge.cc line 862 for 14 seconds the semaphore:
    S-lock on RW-latch at 0x7fc005af2858 created in file dict0dict.cc line 1183
    a writer (thread id 140577049569024) has reserved it in mode exclusive
    number of readers 0, waiters flag 1, lock_word: 0
    Last time read locked in file row0purge.cc line 862
    Last time write locked in file /PATH/TO/mysql-5.7.27/storage/innobase/row/row0mysql.cc line 5106
    --Thread 140577048487680 has waited at ha_innodb.cc line 5614 for 11 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    (...省略)

    View Slide

  23. DROP DATABASEが取るDICT_SYS Mutex

    DROP DATABASE ... がrow0mysql.cc line 5106 を呼ぶまで
    23

    View Slide

  24. row_drop_database_for_mysql()

    row0mysql.cc row_drop_database_for_mysql()の中⾝

    DROP DATABASEの実⾏中はdict_sys mutexがロックされる
    24
    5071 row_drop_database_for_mysql() {
    5106 row_mysql_lock_data_dictionary(trx);
    5107
    5108 while ((table_name = dict_get_first_table_name_in_db(name))) {
    5192 err = row_drop_table_for_mysql(table_name, trx, TRUE);
    5193 trx_commit_for_mysql(trx);
    5206 }
    5224 row_mysql_unlock_data_dictionary(trx);
    5229 }
    (Mutex Lock取得)
    (Mutex Lock解放)
    (テーブルをループ)
    (テーブル削除)

    View Slide

  25. dict_sys Mutex?

    Data dictionaryを保護するための排他ロック
    • https://github.com/mysql/mysql-server/blob/mysql-
    5.7.27/storage/innobase/include/dict0dict.h#L1695
    25
    /** the dictionary system */
    extern dict_sys_t* dict_sys;
    ...
    /* Dictionary system struct */
    struct dict_sys_t{
    DictSysMutex mutex; /*!< mutex protecting the data
    dictionary; protects also the
    disk-based dictionary system tables;
    this mutex serializes CREATE TABLE
    and DROP TABLE, as well as reading
    the dictionary dat

    View Slide

  26. DELETE⽂のとるDICT_SYS Mutex

    最初に⾒たMutexの情報がDELETE処理

    ロック待ちする直前は…
    • run_before_dml_hook()経由のha_innobase::get_foreign_key_list()
    ----------
    SEMAPHORES
    ----------
    OS WAIT ARRAY INFO: reservation count 2469847
    --Thread 140577082132224 has waited at ha_innodb.cc line 15015 for 13 seconds the semaphore:
    Mutex at 0x7fdaa4b5a9d8, Mutex DICT_SYS created dict0dict.cc:1172, lock var 1
    (...省略)
    Thread 9 (Thread 0x7fdaa70b4700 (LWP 3499)):
    (省略)
    #7 PolicyMutex >::enter (this=0x7fdaa4b5a9d8, n_spins=30, n_delay=6, name=0x1617ee0 "/PATH/TO/mysql-5.7.27/storage/…
    #8 0x0000000000ff5aec in ha_innobase::get_foreign_key_list (this=0x7fbf3ac78030, thd=0x7fbf3c816000, f_key_list=0x7fdaa70b1e20) at /PATH/TO/mysql-5.7.27/…
    #9 0x0000000000c841ff in has_cascade_foreign_key (table=, thd=) at /PATH/TO/mysql-5.7.27/sql/rpl_handler.cc:343
    #10 0x0000000000c84d80 in Trans_delegate::prepare_table_info (this=, thd=0x7fbf3c816000, [email protected]: 0x0, number_of_...
    #11 0x0000000000c86038 in Trans_delegate::before_dml (this=0x1e770a0 , thd=, [email protected]: 0) at …
    #12 0x0000000000cc0af1 in run_before_dml_hook (thd=) at /PATH/TO/mysql-5.7.27/sql/sql_base.cc:5334
    #13 0x0000000000e9aa4d in Sql_cmd_delete::mysql_delete (this=0x7fbf3c825fb8, thd=0x7fbf3c816000, limit=18446744073709551615) at /PATH/TO/mysql-5.7.27…
    (省略) 26

    View Slide

  27. run_before_dml_hook()とは?

    ⼿元でビルドした環境だとRUN_HOOKは呼ばれない!?
    • こ
    のあたりから
    読ん
    でい

    int run_before_dml_hook(THD *thd)
    {
    int out_value= 0;
    (void) RUN_HOOK(transaction, before_dml, (thd, out_value));
    if (out_value)
    my_error(ER_BEFORE_DML_VALIDATION_ERROR, MYF(0));
    return out_value;
    }
    27

    View Slide

  28. RUN_HOOK()マクロ

    RUN_HOOKマクロ
    • https://github.com/mysql/mysql-server/blob/mysql-
    5.7.27/sql/rpl_handler.h#L325
    • ##はマクロ内のトークン連結演算⼦
    • 前出のrun_before_dml_hookの呼び出しと合わせると、以下のように
    呼び出される(はず)
    #define RUN_HOOK(group, hook, args)
    (group ##_delegate->is_empty() ? \
    0 : group ##_delegate->hook args)
    transaction_delege->is_empty() ? \
    0 : transaction_delegate->before_dml(thd, out_value))
    28

    View Slide

  29. Transaction_deleate->is_empty()
    • 宣⾔部

    • https://github.com/mysql/mysql-server/blob/mysql-
    5.7.27/sql/rpl_handler.h#L168

    Trans_delegateクラスでis_empty()の



    ーラ

    ドはな
    く、

    クラスのDelegateクラスを⾒る
    • https://github.com/mysql/mysql-server/blob/mysql-
    5.7.27/sql/rpl_handler.h#L101
    inline bool is_empty()
    {
    DBUG_PRINT("debug", ("is_empty: %d", observer_info_list.is_empty()));
    return observer_info_list.is_empty();
    }
    29

    View Slide

  30. observer_info_list.is_empty()

    Obser
    v
    er_info_listはList
    <>
    • typedef List Observer_info_list;

    Obser
    v
    er_infoにはinstallされたpluginが

    っている
    • gdbで刺してみるとSemi-syncレプリのプラグインが⾒つかる
    • リストへの追加と削除はDelegateクラスのadd_observer(),
    remove_observer()
    class Observer_info {
    public:
    void *observer;
    st_plugin_int *plugin_int;
    plugin_ref plugin;
    Observer_info(void *ob, st_plugin_int *p);
    }; 30

    View Slide

  31. RUN_HOOKは何者なのか?
    • サ


    プラグ

    ンがDMLの前に
    任意
    のコードを実⾏できるよ

    にするフック
    ポイ
    ント
    • 準同期レプリケーションのときだけFKをチェックするのかわからず
    • https://github.com/mysql/mysql-server/blob/mysql-
    5.7.27/sql/sql_base.cc#L5319
    31
    Run the server hook called "before_dml". This is a hook originated from
    replication that allow server plugins to execute code before any DML
    instruction is executed.

    View Slide

  32. 結論

    DROP DATABASEではテー

    ルを1つずつ削除する間

    dict_sys mutexを

    りっ

    なし
    • 準同期
    レプリケーションのプラグ

    ンを

    ンストールしている


    DML実⾏時にRUN_HOOK()
    経由
    でdict_sys mutexを取る
    • インストールしただけで有効化していなくてもロックを取る
    • rpl_semi_sync_master / rpl_semi_sync_slave plugin (5.7表記)
    • こ
    れらが
    競合
    してDROP DATABASE中はDMLがロックされる
    • 単純な⽅法で回避することはできない
    32

    View Slide

  33. 8.0では?
    • has_cascade_foreign_key()
    からha_innobase::get_foreign_key_list()

    呼ばないよ


    修正
    されている
    • 8.0.4から修正されている (コミット)
    • 8.0からはDD API(データディクショナリのAPI)からFKの情報を取れるようにな
    たので、ストレージエンジンからFKリストを取得する必要はなくなったらしい
    • データを⼊れて実験してもDMLが詰まることはなかった
    • has_cascade_foreign_key() のコード
    33

    View Slide

  34. 対策

    8.0にアップグレード!
    • 5.7の最初のGA release(5.7.9)は約7年前(2015/10/21)

    できるだ
    け⼩
    さい
    単位
    でDROP
    • スキーマ単位でDROPしない
    • サイズ


    きいテー

    ルはデータを削除してからDROPする
    34

    View Slide

  35. まとめ
    • 準同期
    レプリケーションプラグ

    ンを

    ンストールしている
    MySQL 5.7.xでは

    DROP DATABASE …が取得するdict_sys
    MutexとDML実⾏時にRUN_HOOK
    経由
    で取得する

    ロックが
    競合


    DMLをつまら


    • こ
    れを
    回避
    するためには
    以下

    ⽅法
    が取れる
    • 8.0にアップグレード
    • TABLE単位でDROPする (TABLEのデータは事前に削除しておく)
    35

    View Slide

  36. MySQLチームメンバー積極採⽤中! (2022/08/02時点)

    https://linecorp.com/
    j
    a/career/position/2377
    36

    View Slide

  37. Q&A
    37

    View Slide

  38. 38

    View Slide

  39. 補⾜資料
    39

    View Slide

  40. Perfによるframeグラフ

    (io_wait, lock待ちの処理は

    まれないため
    参考
    にならず)
    40

    View Slide