データベース システムとファイル システムの最大の違いは、データベースが操作のアトミック性を保証できることです。データベースが停止した場合でも、操作の半分は実行されません。これはデータベース ログであり、これを保証するには完全なクラッシュ回復メカニズムが必要です。この記事では、InnoDB のクラッシュ回復プロセスを注意深く分析しており、コードは 5.6 ブランチに基づいています。
lsn: は、値が大きいほど、データベースが生成したREDOログの量として理解でき、また、データベースが更新された瞬間としても理解できます。アップデートの。さらに、各データ ページには lsn もあり、値が大きいほど、最後に変更されたときの lsn を表します。たとえば、データ ページ A の lsn は 100、データ ページ B の lsn は 200、チェックポイント lsn は 150、システム lsn は 300 です。これは、現在のシステムが 300 に更新され、データ ページが小さくなったことを意味します。 150 を超えるデータがディスクにフラッシュされているため、データ ページ A の最新データはディスク上にある必要がありますが、データ ページ B は必ずしもメモリ内にあるとは限りません。
REDO ログ: たとえば、データを変更するには、まず REDO ログを書き込み、次にデータを書き込みます。 REDO ログの書き込み後、成功がクライアントに直接返されます。このようにすると、ディスクにもう一度書き込まれたように見えますが、ディスクへのランダムな書き込み (データの書き込み) がシーケンシャルな書き込み (REDO ログの書き込み) に変換されるため、パフォーマンスが大幅に向上します。データベースがハングした場合、REDO ログをスキャンすると、フラッシュされていないデータ ページを見つけることができます (クラッシュ前、データ ページはメモリ内で変更されただけで、ディスクに書き込む時間がなかった可能性があります) ) データが失われないようにします。
undo ログ: データベースには、間違ったデータを変更したことがわかった場合に、rollback コマンドを使用して前の操作をロールバックすることもできます。この機能をサポートするには、元に戻すログが必要です。さらに、同時実行性(同じレコード、異なるスレッドによる読み取りは競合しない、読み取りと書き込みは書き込みと読み取りで競合しない、同時書き込みのみ競合する)を向上させるために、InnoDB ではすべて、MVCC と同様のメカニズムを実装します。これも元に戻すログに依存します。統合管理を実現するため、REDO ログとは異なり、UNDO ログはバッファ プール内に対応するデータ ページを持ち、通常のデータ ページと併せて管理され、LRU ルールに従ってメモリから削除され、ディスクから読み込まれます。後で。通常のデータ ページと同様に、元に戻すページを変更するには、最初に REDO ログを書き込む必要があります。
チェックポイント: 英語名はチェックポイントです。データベースのパフォーマンスを向上させるために、メモリが変更されるたびにデータ ページがディスクにフラッシュされるわけではありません。チェックポイント前のデータ ページはディスクにフラッシュされることが保証されているため、以前のログは役に立ちません (InnoDB REDOLOG ログのリサイクルのため、現時点ではログのこの部分が上書きされる可能性があります)。ディスクにフラッシュされるか、ディスクへの書き込みがない可能性があるため、クラッシュ回復中にチェックポイント後のログを使用する必要があります。 InnoDB は、ダーティ ページの更新ステータスに基づいてチェックポイントを定期的に進めるため、データベース クラッシュの回復時間が短縮されます。チェックポイント情報は、最初のログ ファイルの先頭にあります。
クラッシュリカバリ: ユーザーがデータを変更し、成功メッセージを受け取りましたが、データベースの場合、この時点で変更されたデータがダウンロードされていない可能性があります。この時点でデータベースがハングすると、データベースが再起動されます。ユーザーのデータが失われないように、ログから変更されたデータを取り出してディスクに再書き込みする必要があります。ログからデータを取得するこのプロセスは、クラッシュ回復の主なタスクであり、データベースのロールフォワードになる場合もあります。もちろん、クラッシュ回復では、コミットされていないトランザクションをロールバックし、失敗したトランザクションを送信することも必要です。ロールバック操作には UNDO ログのサポートが必要であり、UNDO ログの整合性と信頼性は REDO ログによって保証される必要があるため、クラッシュ リカバリでは最初に REDO ロールフォワードが実行され、次に UNDO ロールバックが実行されます。
データベースクラッシュの回復プロセスをソースコードの観点から注意深く分析してみましょう。プロセス全体はエンジンの初期化フェーズで完了します (innobase_init
),其中最主要的函数是innobase_start_or_create_for_mysql
、innodb はクラッシュ回復を含む、この関数を通じて作成と初期化を完了します。まず、データベースのロールフォワードを紹介します。
ロール フォワード データベースは主に 2 つのステージに分かれています。1 つ目はログ スキャン ステージで、データ ページの space_id と page_no に従って REDO ログを hash_table に分散します。同じデータ ページは同じハッシュ バケット内に分散され、lsn サイズに従って小さいものから大きいものへと並べ替えられます。スキャンが完了すると、ハッシュ テーブル全体が走査され、各データ ページのログが順番に適用され、データ ページのステータスは少なくともクラッシュ前の状態に復元されます。コードを詳しく分析してみましょう。
まず、すべての ibdata ファイルを開きます (open_or_create_data_files
) (複数の ibdata が存在する可能性があります)。これらのファイルの max_flush_lsn と min_flush_lsn を計算します。完全に書き込まれておらず、リカバリする必要があるデータである必要があります。フォローアップ (recv_recovery_from_checkpoint_start_func
) では、checkpont_lsn とこれら 2 つの値を比較して、ibdata をロールフォワードする必要があるかどうかを判断します。 open_or_create_data_files
)(ibdata可以有多个),每个ibdata文件有个flush_lsn在头部,计算出这些文件中的max_flush_lsn和min_flush_lsn,因为ibdata也有可能有数据没写完整,需要恢复,后续(recv_recovery_from_checkpoint_start_func
)通过比较checkpont_lsn和这两个值来确定是否需要对ibdata前滚。
接着,打开系统表空间和日志表空间的所有文件(fil_open_log_and_system_tablespace_files
),防止出现文件句柄不足,清空buffer pool(buf_pool_invalidate
)。接下来就进入最最核心的函数:recv_recovery_from_checkpoint_start_func,注意,即使数据库是正常关闭的,也会进入。
虽然recv_recovery_from_checkpoint_start_func
看过去很冗长,但是很多代码都是为了LOG_ARCHIVE特性而编写的,真正数据崩溃恢复的代码其实不多。
首先,初始化一些变量,查看srv_force_recovery
这个变量,如果用户设置跳过前滚阶段,函数直接返回。
接着,初始化recv_sys
结构,分配hash_table的大小,同时初始化flush list rbtree。recv_sys
结构主要在崩溃恢复前滚阶段使用。hash_table就是之前说的用来存不同数据页日志的哈希表,哈希表的大小被初始化为buffer_size_in_bytes/512, 这个是哈希表最大的长度,超过就存不下了,幸运的是,需要恢复的数据页的个数不会超过这个值,因为buffer poll最多(数据库崩溃之前脏页的上线)只能存放buffer_size_in_bytes/16KB个数据页,即使考虑压缩页,最多也只有buffer_size_in_bytes/1KB个,此外关于这个哈希表内存分配的大小,可以参考bug#53122。flush list rbtree这个主要是为了加入插入脏页列表,InnoDB的flush list必须按照数据页的最老修改lsn(oldest_modifcation)从小到大排序,在数据库正常运行时,可以通过log_sys->mutex和log_sys->log_flush_order_mutex保证顺序,在崩溃恢复则没有这种保证,应用数据的时候,是从第一个元素开始遍历哈希表,不能保证数据页按照最老修改lsn(oldest_modifcation)从小到大排序,这样就需要线性遍历flush_list来寻找插入位置,效率太低,因此引入红黑树,加快查找插入的位置。
接着,从ib_logfile0的头中读取checkpoint信息,主要包括checkpoint_lsn和checkpoint_no。由于InnoDB日志是循环使用的,且最少要有2个,所以ib_logfile0一定存在,把checkpoint信息存在里面很安全,不用担心被删除。checkpoint信息其实会写在文件头的两个地方,两个checkpoint域轮流写。为什么要两个地方轮流写呢?假设只有一个checkpoint域,一直更新这个域,而checkpoint域有512字节(OS_FILE_LOG_BLOCK_SIZE
次に、システム テーブル スペースとログ テーブル スペース (fil_open_log_and_system_tablespace_files
) 内のすべてのファイルを開き、ファイル ハンドルが不足しないようにし、バッファ プールをクリアします (buf_pool_invalidate
)。次に、最もコアな関数であるrecv_recovery_from_checkpoint_start_funcを入力します。データベースが正常に閉じられた場合でも入力されることに注意してください。
recv_recovery_from_checkpoint_start_func
は非常に冗長に見えますが、LOG_ARCHIVE 機能用に多くのコードが記述されており、実際のデータ クラッシュ リカバリ用のコードはあまりありません。 🎜まず、いくつかの変数を初期化し、srv_force_recovery
変数を確認します。ユーザーがロールフォワード フェーズをスキップするように設定した場合、関数は直接戻ります。 🎜次に、recv_sys
構造体を初期化し、hash_table のサイズを割り当て、フラッシュ リスト rbtree を初期化します。 recv_sys
構造体は、主にクラッシュ リカバリのロールフォワード フェーズで使用されます。 hash_table は、前述したように、さまざまなデータ ページのログを保存するために使用されるハッシュ テーブルです。ハッシュ テーブルのサイズは、buffer_size_in_bytes/512 に初期化されます。これが長さを超えると、保存できません。幸いなことに、バッファ ポーリング (データベースがクラッシュする前のオンラインのダーティ ページ) は、圧縮されたページを考慮した場合でも、buffer_size_in_bytes/16KB のデータ ページしか保存できないため、データ ページの数はこの値を超えることはありません。また、このハッシュ テーブルのメモリ割り当てのサイズについては、bug#53122 を参照してください。フラッシュ リスト rbtree は、主にダーティ ページをリストに追加および挿入するために使用されます。データベースが正常に実行されている場合、InnoDB のフラッシュ リストは、データ ページの最も古い変更 lsn (oldest_modifcation) に従って小さいものから大きいものまで並べ替える必要があります。 log_sys->mutex および log_sys->log_flush_order_mutex は順序を保証します。データを適用する場合、ハッシュ テーブルが最初の要素から順に並べ替えられるという保証はありません。最も古い変更 lsn (oldest_modifcation) に応じて大きすぎるため、挿入位置を見つけるには、flush_list を線形に走査する必要があり、非効率すぎるため、挿入位置の検索を高速化するために赤黒ツリーが導入されています。 。 🎜次に、ib_logfile0 のヘッダーからチェックポイント情報を読み取ります。主に Checkpoint_lsn と Checkpoint_no が含まれます。 InnoDB ログは循環的に使用され、少なくとも 2 つ存在する必要があるため、ib_logfile0 にチェックポイント情報を保存しても安全であり、削除される心配はありません。チェックポイント情報は実際にはファイル ヘッダーの 2 か所に書き込まれ、2 つのチェックポイント フィールドが順番に書き込まれます。なぜ 2 か所に順番に書く必要があるのでしょうか?チェックポイント フィールドが 1 つだけあり、このフィールドは常に更新され、チェックポイント フィールドのサイズは 512 バイト (OS_FILE_LOG_BLOCK_SIZE
) であると仮定します。この 512 バイトを書き込むときにデータベースがハングし、サーバーが停止します。ハードウェアのアトミック書き込み機能 (初期のハードウェアにはこの機能がありませんでした) に関係なく、512 バイトの半分しか書き込まれず、チェックポイント ドメイン全体が使用できなくなる可能性があります。このようにすると、データベースはクラッシュ回復を実行できなくなり、起動できなくなります。チェックポイント ドメインが 2 つある場合、1 つが破損しても、もう 1 つを使用して回復を試みることができますが、この時点でログが上書きされている可能性がありますが、少なくとも回復が成功する可能性は高くなります。 2 つのチェックポイント ドメインが順番に書き込まれるため、ディスク セクターの障害の影響も軽減できます。チェックポイント_lsn より前のデータ ページはディスク上に配置されているため、ロールフォワードする必要はありません。後続のデータ ページはまだディスク上に配置されていない可能性があるため、復元する必要があります。 REDO ログは冪等であるため、ディスクに適用され、1 回適用しても 2 回適用しても同様です (基本的な実装: データ ページの lsn が現在の REDO ログの lsn 以上である場合は適用されません。それ以外の場合は適用されません)。 Checkpoint_no は、ディスクがフラッシュされるたびに 1 ずつ増加し、この値は代替書き込みを実装するために使用できます。通常のロジックでは、checkpoint_no の値が最終チェックポイント情報として選択され、後続のクラッシュ リカバリ スキャンの開始点として使用されます。次に、チェックポイント フィールドの情報を使用して、recv_sys 構造体の一部の情報を初期化した後、ログ解析のコア機能に入ります recv_group_scan_log_recs
,这个函数后续我们再分析,主要作用就是解析redo日志,如果内存不够了,就直接调用应用(recv_apply_hashed_log_recs
)日志,然后再接着解析。如果需要应用的日志很少,就仅仅解析分发日志,到recv_recovery_from_checkpoint_finish
函数中在应用日志。
接着,依据当前刷盘的数据页状态做一次checkpoint,因为在recv_group_scan_log_recs
里可能已经应用部分日志了。至此recv_recovery_from_checkpoint_start_func
函数结束。
在recv_recovery_from_checkpoint_finish
函数中,如果srv_force_recovery设置正确,就开始调用函数recv_apply_hashed_log_recs
应用日志,然后等待刷脏的线程退出(线程是崩溃恢复时临时启动的),最后释放recv_sys的相关资源以及hash_table占用的内存。
至此,数据库前滚结束。接下来,我们详细分析一下redo日志解析函数以及redo日志应用函数的实现细节。
解析函数的最上层是recv_group_scan_log_recs
,这个函数调用底层函数(log_group_read_log_seg
),按照RECV_SCAN_SIZE(64KB)大小分批读取。读取出来后,首先通过block_no和lsn之间的关系以及日志checksum判断是否读到了日志最后(所以可以看出,并没一个标记在日志头标记日志的有效位置,完全是按照上述两个条件判断是否到达了日志尾部),如果读到最后则返回(之前说了,即使数据库是正常关闭的,也要走崩溃恢复逻辑,那么在这里就返回了,因为正常关闭的checkpoint值一定是指向日志最后),否则则把日志去头掐尾放到一个recv_sys->buf中,日志头里面存了一些控制信息和checksum值,只是用来校验和定位,在真正的应用中没有用。在放到recv_sys->buf之前,需要检验一下recv_sys->buf有没有满(RECV_PARSING_BUF_SIZE
,2M),满了就报错(如果上一批解析有不完整的日志,日志解析函数不会分发,而是把这些不完整的日志留在recv_sys->buf中,直到解析到完整的日志)。接下的事情就是从recv_sys->buf中解析日志(recv_parse_log_recs
)。日志分两种:single_rec和multi_rec,前者表示只对一个数据页进行一种操作,后者表示对一个或者多个数据页进行多种操作。日志中还包括对应数据页的space_id,page_no,操作的type以及操作的内容(recv_parse_log_rec
)。対応するログを解析した後、space_id と page_no に従ってハッシュし (対応するテーブルスペースがメモリに存在しない場合は、テーブルが削除されたことを意味します)、hash_table (ログの実際の保存場所) に格納します。はまだバッファプール内にあります)これで、後続のアプリケーションを待ちます。ここで注意すべき点がいくつかあります:
multi_rec タイプの場合、MLOG_MULTI_REC_END マークが検出された場合にのみ、ログは完了したとみなされ、hash_table に配布されます。コードを見ると、multi_rec 型のログを 2 回解析していることがわかります。1 回目は整合性を確認するため (MLOG_MULTI_REC_END を探す)、2 回目はログを配布するためにこの点が最適化できると感じます。
現在、50 種類を超えるログ操作があり、各操作の背後にある内容が異なるため、長さも異なります。現在のログ解析ロジックでは、すべての内容を順番に解析してから長さを決定する必要があります。次のログの開始位置を見つけます。実際、この方法は少し非効率的ですが、各操作のヘッダーにフィールドを追加して、後続のコンテンツの長さを保存することができます。これにより、解析速度が向上し、解析速度がさらに向上します。結果から、速度が 2 倍になる可能性があります (38 秒から 14 秒、詳細についてはバグ #82937 を参照)。
チェックポイント後にログがまだ残っていることがわかった場合は、データベースが以前に適切にシャットダウンされておらず、クラッシュ リカバリを実行する必要があることを意味するため、追加の操作 (recv_init_crash_recovery) を実行する必要があります。コード>)、エラー ログに「データベースが正常にシャットダウンされませんでした!」および「クラッシュ リカバリを開始しています。」などの一般的なエラーが出力され、リカバリが必要な場合はデータ ページが二重書き込みバッファから書き込まれているかどうかを確認します。 (<code>buf_dblwr_process
)、アプリケーション ログによって生成されたダーティ ページをフラッシュするためにスレッドも開始する必要があります (この時点では buf_flush_page_cleaner_thread が開始されていないため)。最後に、すべてのテーブルスペースを開く必要があります。 。すべてテーブルであることに注意してください。 。 。 Alibaba Cloud RDS MySQL の運用と保守において、クラッシュ回復段階でデータベースがハングアップすることがよくあり、エラー ログに「.ibd ファイルからテーブルスペース情報を読み取っています...」のような文字が表示されます。これは、データベースがすべてのテーブルを開いていることを確認し、テーブルの数を確認すると、数十、さらには数百万のテーブルがあることがわかりました。 。 。データベースがすべてのテーブルを開く必要がある理由は、ログを分散するときに、space_id がどの ibd ファイルに対応するかを判断する必要があるためです。これは、すべてのテーブルを開き、space_id 情報を読み取ることで判断するためです。もう 1 つの理由は、二重書き込みを容易にするためです。書きかけのデータをチェックするためのバッファ。テーブルが多すぎるとリカバリが遅すぎるという問題を解決するために、MySQL 5.7 が最適化されました (WL#7142)。主なアイデアは、操作が完了したことを示す新しいログ mlog_file_name (space_id と filename のマッピングを含む) を書き込むことです。このテーブルに対する後続の操作では、この新しいログを書き込む必要はありません。クラッシュ回復が必要な場合は、 mlog_file_name を収集することでどのテーブルが変更されたかを確認するためにもう一度スキャンを実行できます。 space_id を決定するすべてのテーブル。 recv_init_crash_recovery
),比如在错误日志中打印我们常见的“Database was not shutdown normally!”和“Starting crash recovery.”,还要从double write buffer中检查是否发生了数据页半写,如果有需要恢复(buf_dblwr_process
),还需要启动一个线程用来刷新应用日志产生的脏页(因为这个时候buf_flush_page_cleaner_thread还没有启动)。最后还需要打开所有的表空间。。注意是所有的表。。。我们在阿里云RDS MySQL的运维中,常常发现数据库hang在了崩溃恢复阶段,在错误日志中有类似“Reading tablespace information from the .ibd files...”字样,这就表示数据库正在打开所有的表,然后一看表的数量,发现有几十甚至上百万张表。。。数据库之所以要打开所有的表,是因为在分发日志的时候,需要确定space_id对应哪个ibd文件,通过打开所有的表,读取space_id信息来确定,另外一个原因是方便double write buffer检查半写数据页。针对这个表数量过多导致恢复过慢的问题,MySQL 5.7做了优化,WL#7142, 主要思想就是在每次checkpoint后,在第一次修改某个表时,先写一个新日志mlog_file_name(包括space_id和filename的映射),来表示对这个表进行了操作,后续对这个表的操作就不用写这个新日志了,当需要崩溃恢复时候,多一次扫描,通过搜集mlog_file_name来确定哪些表被修改过,这样就不需要打开所有的表来确定space_id了。
最后一个值得注意的地方是内存。之前说过,如果有太多的日志已经被分发,占用了太多的内存,日志解析函数会在适当的时候应用日志,而不是等到最后才一起应用。那么问题来了,使用了多大的内存就会出发应用日志逻辑。答案是:buffer_pool_size_in_bytes - 512 * buffer_pool_instance_num * 16KB。由于buffer_pool_instance_num一般不会太大,所以可以任务,buffer pool的大部分内存都被用来存放日志。剩下的那些主要留给应用日志时读取的数据页,因为目前来说日志应用是单线程的,读取一个日志,把所有日志应用完,然后就可以刷回磁盘了,不需要太多的内存。
应用日志的上层函数为recv_apply_hashed_log_recs
(应用日志也可能在io_helper函数中进行),主要作用就是遍历hash_table,从磁盘读取对每个数据页,依次应用哈希桶中的日志。应用完所有的日志后,如果需要则把buffer_pool的页面都刷盘,毕竟空间有限。有以下几点值得注意:
同一个数据页的日志必须按照lsn从小到大应用,否则数据会被覆盖。只应用redo日志lsn大于page_lsn的日志,只有这些日志需要重做,其余的忽略。应用完日志后,把脏页加入脏页列表,由于脏页列表是按照最老修改lsn(oldest_modification)来排序的,这里通过引入一颗红黑树来加速查找插入的位置,时间复杂度从之前的线性查找降为对数级别。
当需要某个数据页的时候,如果发现其没有在Buffer Pool中,则会查看这个数据页周围32个数据页,是否也需要做恢复,如果需要则可以一起读取出来,相当于做了一次io合并,减少io操作(recv_read_in_area
)。由于这个是异步读取,所以最终应用日志的活儿是由io_helper线程来做的(buf_page_io_complete
),此外,为了防止短时间发起太多的io,在代码中加了流量控制的逻辑(buf_read_recv_pages
)。如果发现某个数据页在内存中,则直接调用recv_recover_page
recv_apply_hashed_log_recs
です (アプリケーション ログは io_helper 関数でも実行できます)。 hash_table を走査し、ディスクから読み取ります。各データ ページを取得し、ハッシュ バケット内のログを順番に適用します。すべてのログを適用した後、必要に応じてすべてのbuffer_pool ページをフラッシュします。結局のところ、スペースは限られています。次の点に注意してください: 🎜recv_read_in_area
) を減らすために IO マージを実行したことによるものです。これは非同期読み取りであるため、最後のアプリケーション ログ作業は io_helper スレッド (buf_page_io_complete
) によって行われます。また、短時間に過剰な ios が開始されるのを防ぐために、トラフィックが追加されます。コードに追加します (buf_read_recv_pages
)。データ ページがメモリ内にあることが判明した場合、recv_recover_page
アプリケーション ログが直接呼び出されます。このことから、InnoDB アプリケーション ログは実際にはシングルスレッドのアプリケーション ログではなく、クラッシュ回復用のメイン スレッドに加えて、io_helper スレッドも回復に参加していることがわかります。同時スレッドの数は、io_helper の読み取りスレッドの数によって異なります。 🎜🎜🎜🎜 REDO ロールフォワード データベースの実行後、データベースのすべてのデータ ページはすでに整合性のある状態になっており、UNDO ロールバック データベースを安全に実行できます。データベースがクラッシュすると、コミットされていないトランザクションまたはコミットされたトランザクションが存在する可能性があります。この時点で、コミットするかどうかを決定する必要があります。これは主に 3 つのステップに分かれています。まず、アンドゥ ログをスキャンし、アンドゥ ログのリンク リストを再構築します。次に、前のステップで確立されたリンク リストに基づいて、クラッシュ前のトランザクションを再構築します。つまり、状態を復元します。その時の取引内容です。最後に、トランザクションのさまざまなステータスに基づいてロールバックまたはコミットが実行されます。 🎜 を元に戻す)。ここで注意すべき点は、トランザクションが TRX_STATE_PREPARED 状態にある場合、サーバー層はバイナリログの状況に基づいてトランザクションをロールバックするかどうかを決定する必要がないことです。バイナリ ログが書き込まれているため、トランザクションはスタンバイ データベースに転送される可能性があります。バイナリ ログが書き込まれていないと、プライマリ データとスタンバイ データが不整合になります。巻き戻された。 recv_recovery_from_checkpoint_start_func
之后,recv_recovery_from_checkpoint_finish
之前,调用了trx_sys_init_at_db_start
,这个函数做了上述三步中的前两步。
第一步在函数trx_rseg_array_init
中处理,遍历整个undo日志空间(最多TRX_SYS_N_RSEGS(128)个segment),如果发现某个undo segment非空,就进行初始化(trx_rseg_create_instance
)。整个每个undo segment,如果发现undo slot非空(最多TRX_RSEG_N_SLOTS(1024)个slot),也就行初始化(trx_undo_lists_init
)。在初始化undo slot后,就把不同类型的undo日志放到不同链表中(trx_undo_mem_create_at_db_start
)。undo日志主要分为两种:TRX_UNDO_INSERT和TRX_UNDO_UPDATE。前者主要是提供给insert操作用的,后者是给update和delete操作使用。之前说过,undo日志有两种作用,事务回滚时候用和MVCC快照读取时候用。由于insert的数据不需要提供给其他线程用,所以只要事务提交,就可以删除TRX_UNDO_INSERT类型的undo日志。TRX_UNDO_UPDATE在事务提交后还不能删除,需要保证没有快照使用它的时候,才能通过后台的purge线程清理。
第二步在函数trx_lists_init_at_db_start
中进行,由于第一步中,已经在内存中建立起了undo_insert_list和undo_update_list(链表每个undo segment独立),所以这一步只需要遍历所有链表,重建起事务的状态(trx_resurrect_insert
和trx_resurrect_update
)。简单的说,如果undo日志的状态是TRX_UNDO_ACTIVE,则事务的状态为TRX_ACTIVE,如果undo日志的状态是TRX_UNDO_PREPARED,则事务的状态为TRX_PREPARED。这里还要考虑变量srv_force_recovery的设置,如果这个变量值为非0,所有的事务都会回滚(即事务被设置为TRX_ACTIVE),即使事务的状态应该为TRX_STATE_PREPARED。重建起事务后,按照事务id加入到trx_sys->trx_list链表中。最后,在函数trx_sys_init_at_db_start
中,会统计所有需要回滚的事务(事务状态为TRX_ACTIVE)一共需要回滚多少行数据,输出到错误日志中,类似:5 transaction(s) which must be rolled back or cleaned up。InnoDB: in total 342232 row operations to undo的字样。
第三步的操作在两个地方被调用。一个是在recv_recovery_from_checkpoint_finish
的最后,另外一个是在recv_recovery_rollback_active
中。前者主要是回滚对数据字典的操作,也就是回滚DDL语句的操作,后者是回滚DML语句。前者是在数据库可提供服务之前必须完成,后者则可以在数据库提供服务(也即是崩溃恢复结束)之后继续进行(通过新开一个后台线程trx_rollback_or_clean_all_recovered
来处理)。因为InnoDB认为数据字典是最重要的,必须要回滚到一致的状态才行,而用户表的数据可以稍微慢一点,对外提供服务后,慢慢恢复即可。因此我们常常在会发现数据库已经启动起来了,然后错误日志中还在不断的打印回滚事务的信息。事务回滚的核心函数是trx_rollback_or_clean_recovered
,逻辑很简单,只需要遍历trx_sys->trx_list,按照事务不同的状态回滚或者提交即可(trx_rollback_resurrected
innodb_fast_shutdown: innodb_fast_shutdown = 0。これは、MySQL がシャットダウンされるときに、スロー シャットダウンが実行されることを意味します。これには、ログのフラッシュとデータ ページのフラッシュだけでなく、データ クリーニング (パージ)、ibuf マージ、バッファ プール ダンプ、および遅延テーブル ドロップ操作 ( table 未完了の操作があります。drop table を実行して復帰しても、テーブルがすぐに削除されない場合があります。
innodb_fast_shutdown = 1。これはデフォルト値であり、MySQL がシャットダウンされると、ログとデータのみがフラッシュされることを意味します。
innodb_fast_shutdown = 2。これは、MySQL がクラッシュしたかのように、閉じられるとログのみがフラッシュされ、他には何も行われないことを意味します。
このパラメータの値が大きいほど、MySQL のシャットダウンは速くなりますが、起動速度は遅くなります。これは、シャットダウン中に実行する必要がある作業をクラッシュ回復に移すことと同じです。さらに、MySQL をアップグレードする場合は、最初の方法を使用してクリーン シャットダウンを実行することをお勧めします。
innodb_force_recovery:
このパラメータは主に、InnoDB の起動時に実行する作業を制御するために使用されます。値が大きいほど、実行される作業が少なくなり、起動が容易になりますが、データの不整合のリスクも高くなります。制御不能な理由で MySQL を起動できない場合は、このパラメータを設定し、MySQL が起動するまで 1 から徐々に増やし、その後 SELECT INTO OUTFILE を使用してデータをエクスポートし、データ損失を減らすために最善を尽くします。
innodb_force_recovery=0。これはデフォルトのパラメータで、REDO ログの適用、UNDO ログのロールバック、バックグラウンド マスターとパージ スレッドの開始、ibuf のマージなど、起動時にすべての処理を実行します。データ ページがシステム テーブル スペースにある場合、データ ページが破損していることが検出され、ユーザー テーブル スペースにある場合、エラー ログが記録されます。
innodb_force_recovery = 1。データ ページが破損していることが検出された場合、クラッシュやエラーは発生せず (buf_page_io_complete
)、起動時に表スペースの最初のデータ ページの正確性は検証されません ( >fil_check_first_page)、表スペースにアクセスできず、クラッシュ リカバリが続行されます (<code>fil_open_single_table_tablespace
、fil_load_single_table_tablespace
)、DDL 操作を実行できません (check_if_supported_inplace_alter) code>))、同時にデータベースは書き込み操作 (<code>row_insert_for_mysql
、row_update_for_mysql
など) も実行できなくなり、準備されたトランザクションもすべてロールバックされます。 (trx_resurrect_insert
、trx_resurrect_update_in_prepared_state
)。このオプションは依然として非常に一般的に使用されています。これを 1 に設定すると、データベースが正常に起動できるようになります。 buf_page_io_complete
),启动的时候也不会校验表空间第一个数据页的正确性(fil_check_first_page
),表空间无法访问也继续做崩溃恢复(fil_open_single_table_tablespace
、fil_load_single_table_tablespace
),ddl操作不能进行(check_if_supported_inplace_alter
),同时数据库也被不能进行写入操作(row_insert_for_mysql
、row_update_for_mysql
等),所有的prepare事务也会被回滚(trx_resurrect_insert
、trx_resurrect_update_in_prepared_state
)。这个选项还是很常用的,数据页可能是因为磁盘坏了而损坏了,设置为1,能保证数据库正常启动。
innodb_force_recovery = 2。除了设置1之后的操作不会运行,后台的master和purge线程就不会启动了(srv_master_thread
、srv_purge_coordinator_thread
等),当你发现数据库因为这两个线程的原因而无法启动时,可以设置。
innodb_force_recovery = 3。除了设置2之后的操作不会运行,undo回滚数据库也不会进行,但是回滚段依然会被扫描,undo链表也依然会被创建(trx_sys_init_at_db_start
)。srv_read_only_mode会被打开。
innodb_force_recovery = 4。除了设置3之后的操作不会运行,ibuf的操作也不会运行(ibuf_merge_or_delete_for_page
),表信息统计的线程也不会运行(因为一个坏的索引页会导致数据库崩溃)(info_low
、dict_stats_update
等)。从这个选项开始,之后的所有选项,都会损坏数据,慎重使用。
innodb_force_recovery = 5。除了设置4之后的操作不会运行,回滚段也不会被扫描(recv_recovery_rollback_active
),undo链表也不会被创建,这个主要用在undo日志被写坏的情况下。
innodb_force_recovery = 6。除了设置5之后的操作不会运行,数据库前滚操作也不会进行,包括解析和应用(recv_recovery_from_checkpoint_start_func
innodb_force_recovery=2。設定 1 以降の操作が実行されないことに加えて、データベースが見つかった場合、バックグラウンドのマスター スレッドとパージ スレッド (srv_master_thread
、srv_purge_coordinator_thread
など) が開始されません。はこの2つのためです。 スレッドの理由によりスレッドを開始できない場合に設定できます。
trx_sys_init_at_db_start
)。 。 srv_read_only_mode がオンになります。 ibuf_merge_or_delete_for_page
) も実行されず、テーブル情報統計のスレッドも実行されません (インデックス ページが不良であるとデータベースがクラッシュ) (info_low
、dict_stats_update
など)。このオプション以降、後続のすべてのオプションはデータに損傷を与えるため、慎重に使用してください。 recv_recovery_rollback_active
)、および UNDO リンク リストの作成は行われません。これは主に UNDO ログを書き込むときに使用されます。悪い。 innodb_force_recovery=6。設定 5 後の操作に加えて、解析と適用 (recv_recovery_from_checkpoint_start_func
) を含むデータベースのロールフォワード操作は実行されません。
以上がMySQL エンジンの機能と InnoDB のクラッシュ リカバリの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。