著者:データベース技術の専門家でも8人の奇妙な(Gao)
レベルに限りがありますので、エラーがございましたらご了承ください。ソースコードのバージョンは8.0.21です。
障害に対処する場合、大量の削除データが原因でクエリが遅くなっていることが疑われますが、パージスレッドのワークフローについてはよくわかりません。この記事では、詳細な分析は行わず、ワークフロー分析のみを行い、次の質問を待ちます。
デルフラグレコードを時間内にクリーンアップできるかどうか
履歴リストの長さが継続的に0ではないのはなぜですか?delフラグレコードがクリーンアップされていないことを意味しますか?
パージスレッドによってトリガーされるルールは何ですか
1.パージスレッドの概要
一般的に、私たちが理解しているパージスレッドは、次の作業を実行できます。
デルフラグタグのレコードをクリーンアップします
元に戻すの履歴バージョンをクリーンアップします
元に戻すテーブルスペースの切り捨てを実行する必要がある場合。
これには、調整スレッドと、次のパラメーターで設定された複数のワーカースレッドが含まれています。
innodb_purge_threads=4
これは、1つの調整スレッドと3つのワーカースレッドを表します。調整スレッドは、ワーカースレッドとしても機能します。
2つ目は、スレッドループ検出の変更を調整する
如下调入:
srv_purge_coordinator_thread
->srv_purge_coordinator_suspend
判断如下:
(rseg_history_len <= trx_sys->rseg_history_len) {
//如果当前history_len大于等于上一次循环的的history_len
ret =os_event_wait_time_low(slot->event, SRV_PURGE_MAX_TIMEOUT, sig_count);
//等待10毫秒后进行处理或者等待被唤醒
ウェイクアップの条件は、トランザクションのコミットまたはロールバックです。
/* Tell server some activity has happened, since the trx
does changes something. Background utility threads like
master thread, purge thread or page_cleaner thread might
have some work to do. */
srv_active_wake_master_thread();
ただし、長時間コミットする新しいトランザクションがない場合は、ウェイクアップされるまで10ミリ秒ごとにウェイクアップするのではなく、永続的なブロッキング状態になる可能性があることに注意してください。
if (ret == OS_SYNC_TIME_EXCEEDED) { //如果是等待超时
if (rseg_history_len == trx_sys->rseg_history_len &&
trx_sys->rseg_history_len < 5000) { //如果上次的history_len和本次history_len相同且小于5000那么需要等待唤醒
stop = true; //设置为true,进行无限期等待,直到唤醒
}
3つ目は、最も古い読み取りビューのクローンを作成する
現在の最も古い読み取りビューに従って元に戻す必要があるため、この手順については何も言うことはありません。そうしないと、読み取られている元に戻すまでクリーンアップされる可能性があります。
如下调入:
srv_purge_coordinator_thread
->srv_do_purge
->trx_purge
操作如下:
trx_sys->mvcc->clone_oldest_view(&purge_sys->view); //克隆老的 read view srv_do_purge
第4に、クリーンアップが必要な可能性のある元に戻すセグメントをpurge_queueから削除します(単にトランザクションとして理解されます)。
调入如下:
srv_purge_coordinator_thread
->srv_do_purge
->trx_purge
->trx_purge_attach_undo_recs
->trx_purge_fetch_next_rec
->TrxUndoRsegsIterator::set_next
操作如下:
const page_size_t &page_size = purge_sys->rseg_iter->set_next();
これはイテレーターであることに注意してください。反復はpurge_sys-> purge_queueです。これは、std :: priority_queueによって実装される優先キューです。具体的な反復コードは次のとおりです。
while (!m_purge_sys->purge_queue->empty()) { //如果有事务需要清理
if (m_trx_undo_rsegs.get_trx_no() == UINT64_UNDEFINED) {
m_trx_undo_rsegs = purge_sys->purge_queue->top();
} else if (purge_sys->purge_queue->top().get_trx_no() ==
m_trx_undo_rsegs.get_trx_no()) {
m_trx_undo_rsegs.append(purge_sys->purge_queue->top()); //弹出一个
} else {
break;
}
トランザクションがコミットされたときにtrx_serialisation_number_getを呼び出すことにより、トランザクションはpurge_queueに入ります。
purge_sys->purge_queue->push(elem);
したがって、ここでは、トランザクションがコミットされると、パージ調整スレッドが起動して機能するようになり、パージが必要になる可能性のあるトランザクションキューpurge_queueに追加されることがわかります。
5、クリーンアップルールを満たしているかどうかを判断します
调入如下:
srv_purge_coordinator_thread
->srv_do_purge
->trx_purge
->trx_purge_attach_undo_recs
->trx_purge_fetch_next_rec
判断如下:
if (purge_sys->iter.trx_no >= purge_sys->view.low_limit_no()) {
return (nullptr);
}
クリーンアップする必要のあるトランザクションのtrx番号が、最も古い読み取りビューの下限番号よりも大きいかどうかを判断します。満たされていない場合はnullptrを返します。満たされていない場合は、クリーンアップする必要のあるページ数を返し、クリーンアップする必要のある次の元に戻すセグメントを指します。
6.各クリーニングのデフォルトは300ページです
この値は、パラメーターinnodb_purge_batch_sizeによって制御されます。デフォルトは300です。
调入如下:
srv_purge_coordinator_thread
->srv_do_purge
->trx_purge
->trx_purge_attach_undo_recs
生效如下:
for (ulint i = 0; n_pages_handled < batch_size; ++i)
クリーンアップするページがなくなるまで、クリーニングプロセスが続行されます
调入如下:
srv_purge_coordinator_thread
->srv_do_purge
判断如下:
(!srv_purge_should_exit(n_pages_purged) && n_pages_purged > 0 &&
purge_sys->state == PURGE_STATE_RUN);
//清理完成后n_pages_purged > 0 将不会满足
return (rseg_history_len); //返回 rseg_history_len
7、ワーカースレッド処理
ワーカースレッドに配布された後、次の呼び出しを入力してdelフラグをクリーンアップします。この部分を注意深く見ないと、呼び出しはより複雑になります。ただし、その構築row_purge_parse_undo_rec)および削除プロセスでは、多くのループとデータ配置(btr_cur_search_to_nth_level)操作が必要になる場合があることは確かです。
srv_worker_thread
->srv_task_execute
->que_run_threads
->que_run_threads_low
->que_thr_step
->row_purge_step
->row_purge
->row_purge_record_func
8.デフォルトでは、元に戻す履歴は128回のバッチ元に戻すクリーンアップごとにクリーンアップされます
これは、パラメータinnodb_purge_rseg_truncate_frequencyの設定に関連しています。全負荷が次のように計算される場合、デフォルトは128です。
300(ログページを元に戻す)* 128(頻度を切り捨てる)= 38,400
38400の取り消しログページが処理された後、取り消し履歴が1回クリーンアップされます。
根据参数赋值
set_rseg_truncate_frequency(
static_cast<ulint>(srv_purge_rseg_truncate_frequency));
参数判断
ulint rseg_truncate_frequency = ut_min(
static_cast<ulint>(srv_purge_rseg_truncate_frequency), undo_trunc_freq); //128
n_pages_purged = trx_purge(n_use_threads, srv_purge_batch_size,
(++count % rseg_truncate_frequency) == 0);//每128次进行一次清理
判断是否进入truncate流程
if (truncate || srv_upgrade_old_undo_found) { //truncate就是根据(++count % rseg_truncate_frequency)计算而来
trx_purge_truncate();
}
ただし、countは静的ローカル変数であるため、関数が呼び出されるたびに、最後の値がカウントされ続けることに注意してください。圧力が低い場合、元に戻すことが時間内にクリーンアップされない可能性があります。
小さなトランザクションが小さなトランザクションである場合、各トランザクションによって変更される元に戻すページの数は300に達しない可能性があるため、128のトランザクションが一度クリーンアップされるまで待つ必要があります。
大規模なトランザクショントランザクションが比較的大きく、元に戻すページが多い場合、300 * 128を超えるとクリーンアップされます。
これは、delフラグレコードがクリーンアップされないということではなく、取り消し履歴のリンクされたリストがクリーンアップされないということではありません。したがって、履歴リストの長さが0ではないことがよくあります。
9、元に戻す履歴と元に戻すスペースをクリーンアップします
これがその作業プロセスの簡単な記録です。詳細な機能の説明はありません(限られた能力)
清理undo history
调入如下:
srv_purge_coordinator_thread
->srv_do_purge
->trx_purge
->trx_purge_truncate
->trx_purge_truncate_history
->trx_purge_truncate_rseg_history
洗浄方法は以下の通りです。
清理的起点:
hdr_addr = trx_purge_get_log_from_hist(
flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
向上扫描:
hdr_addr = prev_hdr_addr;
结束条件:
if (undo_trx_no >= limit->trx_no) { //这里代表结束了
/* limit space_id should match the rollback segment
space id to avoid freeing if the page belongs to a
different rollback segment for the same trx_no. */
if (undo_trx_no == limit->trx_no &&
rseg->space_id == limit->undo_rseg_space) {
trx_undo_truncate_start(rseg, hdr_addr.page, hdr_addr.boffset,
limit->undo_no);
}
rseg->unlatch();
mtr_commit(&mtr);
return;
}
このクリーンアッププロセスは、最も古い読み取りビューのtrx noを超えることはできません。それ以外の場合、クリーンアップは終了します。
元に戻すプロセスを切り捨てる
调入如下:
srv_purge_coordinator_thread
->srv_do_purge
->trx_purge
->trx_purge_truncate
->trx_purge_truncate_history
->trx_purge_truncate_marked_undo
この前に、クリーンアップするかどうかを決定するプロセスがあります
trx_purge_mark_undo_for_truncate
->Tablespace::needs_truncation
Tablespace :: needs_truncationは、元に戻す切り捨てを実行するかどうかを決定します。関連する2つのパラメーターを次に示します。
パラメータinnodb_undo_log_truncateの役割
if (!srv_undo_log_truncate || m_rsegs == nullptr || m_rsegs->is_empty() ||
m_rsegs->is_init()) {
m_rsegs->s_unlock();
return (false); //如果没有开启undo truncate则不进行清理
}
パラメータinnodb_max_undo_log_sizeの役割
page_no_t trunc_size = ut_max(
static_cast<page_no_t>(srv_max_undo_tablespace_size / srv_page_size),
static_cast<page_no_t>(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES)); //10MB
if (fil_space_get_size(id()) > trunc_size) { //如果undo tablespace大小大于了innodb_max_undo_log_size
return (true); //则进行清理
}
10.まとめ
この最初の問題は、基本的に次のように理解しています。
トランザクションが送信された後、調整スレッドはdelフラグをクリーンアップできるかどうかを判断します。クリーンアップできる場合は、クリーンアップのためにワーカースレッドに配布されます。これは非同期プロセスです。データの変更が多い場合、このプロセスは遅くなる可能性があり、確認できます。パージに関連するスレッドには圧力がかかっていますが、それでもタイムリーです。
パージスレッドは、履歴リストの長さをクリーンアップするために、常に一定期間バックログされます。それが小さなトランザクションの場合(毎回変更されるページがinnodb_purge_batch_sizeの設定よりも小さい場合)、128のそのような小さなトランザクションは、一度クリーンアップする必要があります。大きなトランザクションの場合は、変更します。設定が(innodb_purge_batch_size * innodb_purge_rseg_truncate_frequency)を超えると、一度クリーンアップされますが、このインジケーターがゼロ以外のままであっても、正常です。それが大きい場合は、クエリが大きいか、パージの各スレッドが最大容量で動作していることを意味している可能性があります。次のように、9281はパージワーカースレッドです。
そして、パージスレッドの状態は実行状態です
パージの調整スレッドは、トランザクションがコミットされるたびにウェイクアップして、クリーンアップが必要なトランザクションがあるかどうかを判断します。トランザクションが長時間ない場合は、最初の10ミリ秒待機し、タイムアウト後に長期ブロッキング待機状態に入ります。
全文は終わりました。
MySQLをお楽しみください:)
コードをスキャンして作成者WeChatを追加します
TeacherYeの「MySQLCoreOptimization」クラスがMySQL8.0にアップグレードされました。コードをスキャンして、MySQL8.0の練習の旅を始めてください。