【MySQL Innodbアーキテクチャの分析】 1.メモリアーキテクチャ

この記事の内容は主に MySQL5.7 公式 Web サイトのマニュアル - Innodb メモリ アーキテクチャ部分を手動で翻訳したもので、読者は公式マニュアルと併せて読むことができます。間違いがあればご指摘ください。読んでいただきありがとうございます。議論も歓迎です。

1. Innodb アーキテクチャ図 (MySQLv5.7 は公式 Web サイトから引用)

ここに画像の説明を挿入
説明: 上の図にはInnodb のメモリ アーキテクチャディスク アーキテクチャが含まれており、それぞれについては後で詳しく説明します。

1.1 Innodb メモリ アーキテクチャ

主にいくつかのポイントに分けて

  • バッファプール(バッファ池)
  • バッファの変更
  • アダプティブ ハッシュ インデックス (アダプティブ ハッシュ インデックス)
  • ログバッファ (ログバッファ)

注: 通常、バッファは変換されません。

1.2 バッファプール

: 記述の便宜上、以下ではバッファ プールを指すために bp を使用します。

バッファプールは、テーブルとインデックスデータのメモリ領域を保存/変更/アクセスするためにmysqlランタイムによって使用されるメモリ領域です。MySQL は、頻繁にアクセスされるデータを保存するためにこれを使用します。
理論的には、使用できるメモリ空間が大きいほど、mysql のパフォーマンスが向上するため、専用の mysql サーバーでは、通常、物理メモリの 80% がバッファ プールに割り当てられます。

大容量データのアクセス性能を向上させるため、bpは内部で複数行を収容できるページ(Pages)に分割されています。
キャッシュされたデータの管理を容易にするために、bp はページで構成されるリンク リスト (リンク リスト) を実装しており、データの有効期限戦略は LRU アルゴリズムを使用して実装されています。

頻繁にアクセスされるデータをメモリ内に保持するバッファ プールの使用方法を知ることは、MySQL チューニングの重要な側面です。

新リンクリストと旧リンクリスト(以下、新リンクリストと旧リンクリストという)

単一のリンク リストに基づいて、bp は新しいサブリンク リストと古いサブリンク リストを分割します。

  • 新しいサブリンク リストは単一リンク リストの先頭にあり、5/8 を占め、古いサブリンク リストは最後にあり、3/8 を占めています。
  • 新しい部分は頻繁にアクセスされる部分、古い部分はその逆です
  • 新しいものと古いものは別々に管理されます

下の画像はmysql公式サイトからのものです
新旧のリンクリスト構造

  • 図の中点が新旧の分岐点です
  • ディスクから読み取られたデータがリンクリストに挿入されるとき、初めて古いサブリンクリストの先頭に挿入され、古いサブリンクリストのデータが先頭に移動されます。アクセスされたときの新しいサブリンク リスト (先読み操作を除く)
  • 新しいサブリストと古いサブリスト内のページは、他のページが更新されるにつれて古くなり、未使用またはあまり使用されていないページは徐々に古いサブリストの最後に到達し、その後削除されます。

なぜ二重リンクリストを使用するのでしょうか?

単一リンクリストの場合のバッファプール汚染

  1. デフォルトのメカニズムでは、ページが読み取られる限り、リンクされたリストの先頭に移動します。これにより、場合によってはバッファ プールの汚染が発生する可能性があります。たとえば、WHERE を指定しない mysqldump 操作または SELECT クエリは、一度に大量のデータを bp ページのリンク リストに保存し、同じ量のデータが古くなって無効になります。そして、これらの操作は一時的なものであり、読み取られた大量のデータは長期間再び読み取られないため、bp プール汚染が発生し、mysql のパフォーマンスが大幅に低下します。
  2. 同じ原理で、先読み操作も bp プールの汚染を引き起こします。

ダブルリンクリスト方式

  1. 新しいリンク リストと古いリンク リストを区別し、ディスクからロードされたばかりのデータを保存するために古いリンク リストを使用します。
  2. どのような操作であっても、データはディスクからロードされた後、まず古いリンク リストに保存されます。これらのデータが削除される前に再度読み取られる場合、それは実際にホット データであるため、次の場所に移動する必要があることを意味します。新しいリンク リストの先頭です。そうでない場合は、常に古いリンク リストに含まれます。古いリンク リストは、新しいリンク リストよりも速い速度で削除されます。
  • バッファプールのステータスを監視する
mysql> SHOW ENGINE INNODB STATUS

このコマンドの出力はさらに、bp プールのデータのBUFFER POOL AND MEMORY一部であり、次のデータは実環境から傍受されます。

BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137363456    // buffer pool总大小,所有空间的单位都是字节,这里是137MB左右
Dictionary memory allocated 16255176 // 为innodb字典分配的内存大小
Buffer pool size   8191 // 能容纳的page数量
Free buffers       1024 // free链表中有多少个空闲页
Database pages     6983 // LRU链表的总数据页数量
Old database pages 2557 // LRU链表中old链表的页数量
Modified db pages  191 // flush链表中的页数量
Pending reads 0 //  等待从磁盘加载的缓存页数量
Pending writes: LRU 0, flush list 0, single page 0  // 即将从LRU以及flush链表中刷入磁盘的数量,single page是buffer pool中正在写入的页数量
Pages made young 13484194, not young 8408292 // young表示LRU链表中从old迁移到new链表的页总数,后者是停留在old链表的缓存页数;这里的数值应该都是累计值。
400.95 youngs/s, 1.25 non-youngs/s // youngs/s是平均每秒从old链表移动到new链表的页数,后者是平均每秒old链表中被访问了但没有移动到new链表的页数
Pages read 411197, created 182724, written 5421234 // 从mysql启动到现在一共读取、创建、写入了的页数
1.12 reads/s, 0.12 creates/s, 7.37 writes/s // 平均每秒读取、创建、写入的页数
Buffer pool hit rate 1000/ 1000, young-making rate 16 / 1000 not 0 / 1000 // 平均每1000次访问mysql,多少次是命中buffer pool的,其中有多少页是访问后迁移到new链表的,多少次是访问old链表页但没迁移的(大部分应该都是访问的new链表)
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s // 预读缓存页的速度,淘汰未访问数据页的数量,随机预读速度
LRU len: 6983, unzip_LRU len: 0 // LRU链表中的总页数,unzip_LRU链表的总页数
I/O sum[369]:cur[0] // sum是缓冲池 LRU链表中被访问的数据页总数(直译),cur是刚刚这段时间间隙内访问的页数
unzip sum[0]:cur[0] //  前者是缓冲池 unzip_LRU链表解压缩的页总数(直译),后者是刚刚这段时间间隙内解压缩的页数

ブロガーは、最終的な I/O 合計値の解釈に疑問を持っています。データは実際の環境からのものです。この値は、「若いページ数」よりもはるかに小さいです。ブロガーがインターネット上で見つけた別の説明: の合計数過去 50 秒以内に読み取られたディスク ページ (より信頼できるようですが、公式には同様の説明が見つかりませんでした)

  • バッファプール構成のチューニング
    • 大きなメモリ空間を持つホストを使用し、mysql のバッファ プール サイズをより大きく設定します。mysql の操作のほとんどはメモリ内で実行されるため、ほとんどのデータはディスクから 1 回ロードされるだけで、後続のメモリが読み取られるため、bp サイズが大きいほどより多くのデータをメモリに保存でき、パフォーマンスが向上します。もっと高い!変数は、innodb_buffer_pool_size 公式 Web サイトで構成を表示することです(構成方法は自分で確認してください。この記事では繰り返しません)。
    • 64 ビット システムと大容量メモリを備えたサーバーの場合は、複数のバッファ プール インスタンス (インスタンス) を構成することをお勧めします。これにより、メモリ内のデータ ページの読み取りと書き込みの競合が軽減され、同時実行パフォーマンスが向上します。関連する変数は ですinnodb_buffer_pool_instancesが、公式の要件は bp サイズ > 1GB であるため、このパラメータは有効になります。公式 Web サイトの設定を確認してください。
    • アクセス頻度の低いデータをバッファプールに大量に持ち込む操作を考慮せずに、頻繁にアクセスされるデータをメモリ上に保持できます。これは二重リンクリストに基づいています。関連する変数は次のとおりです。公式 Web サイトの設定を参照してinnodb_old_blocks_pctくださいinnodb_old_blocks_time
    • バッファ プールにページを非同期的にプリフェッチするための先読み (プリフェッチまたは先読みと呼ばれる) リクエストをいつどのように実行するかを制御できます。関連する変数は次のとおりです。公式 Web サイトの設定を参照してくださいinnodb_read_ahead_thresholdinnodb_random_read_ahead
    • ダーティ ページ データのフラッシュのタイミングを bp で制御し、リアルタイムの負荷に応じてフラッシュ レートを自動的に調整するかどうかを決定できます。関連する変数は、 、 などです。公式Webinnodb_page_cleanersサイトinnodb_max_dirty_pages_pct_lwminnodb_max_dirty_pages_pct設定を確認してください。
    • bp が現在のバッファ プールの状態を保存する方法を制御できます。適切な設定を行うことで、次回 mysql を開始した後の長期的なウォームアップを回避できます。関連する設定変数が多数ありinnodb_buffer_pool_dump_pctます(などを含むinnodb_buffer_pool_dump_at_shutdown) 。

1.3 バッファの変更

Change Buffer (以下、CB) は、複数のセカンダリ インデックス ページと、バッファ内に存在しないセカンダリ インデックス ページをキャッシュするために使用される B+ ツリー (共有テーブル スペース、ibdata1 ファイルに存在) で構成されるメモリ空間です。インデックス ページに対する DML 操作 (INSERT、UPDATE、DELETE)、およびその後、これらの変更はさまざまなメカニズムに従ってバッファ プールにマージされます。

次の図は、CB とバッファー プールの間の相互作用です
ここに画像の説明を挿入
。Change Buffer の内部実装
です。前述したように、CB は、すべてのテーブルのセカンダリ インデックスの変更を記録する役割を担う B+ ツリーで構成されています。ツリーの非リーフ ノードには、次の図に示すように構造化された検索キーが格納されます。検索キーは
ここに画像の説明を挿入
合計 9 バイトを占め、スペースは挿入されるレコードが含まれるテーブルのテーブル スペース ID を表します。 InnoDB ストレージ エンジンでは、各テーブルに一意のスペース ID があり、スペース ID クエリを通じてテーブルがどのテーブルであるかをクエリできます。スペースは 4 バイトを占めます。マーカーは 1 バイトを占有し、古いバージョンの挿入バッファと互換性を保つために使用されます。offset はページが配置されているオフセットを示し、4 バイトを占めます。
セカンダリ インデックス レコードがページ (スペース、オフセット) に挿入されるとき、そのページがバッファ プールにない場合、InnoDB エンジンはまず上記のルールに従って検索キーを構築し、次に変更バッファの B+ ツリーにクエリを実行します。を挿入し、このレコードは変更バッファ B+ ツリーのリーフ ノードに挿入されます。
Change Buffer B+ ツリーのリーフ ノードに挿入されるレコードの場合、挿入されるレコードは直接挿入されるのではなく、次のルールに従って構築される必要があります。最初のいくつかのフィールドは検索キーと同じであり、メタデータは4
ここに画像の説明を挿入
バイトを占め、リプレイリカバリデータ用にこのツリーに挿入されたシーケンス値など、この変更レコードのメタデータを記録します。その他は、DB 開発を行わない場合はマスターする必要はありません。セカンダリ インデックス レコードの次の部分には、この操作 (DML) の内容が記述されます。

1 つ注意してください: CB はインデックス変更データのみを保存し、ソース データ変更は保存しません。そのため、データを更新するときに、変更するデータ ページがバッファ プールに存在しない場合は、ディスクに移動してソース データ ページをロードする必要があります。 。データの変更が完了したら、インデックスを更新します (フィールドにインデックスがある場合)。


  • なぜ Change Buffer実験 SQLが必要なのでしょうかupdate x=1 from t where id=1: , ここでは、id が主キーであり、x が共通 (副) インデックス フィールドであると仮定します。
    まず、CB がないことを前提として、mysql の UPDATE の一般的なロジックを知る必要があります。操作 (UNDO REDO ログは途中で省略され、リンクはロックされています):
    フェーズ 1: まずクラスター化インデックス ページを更新します
    1. id は主キーであるため、バッファー プール内のキャッシュされたクラスター (主キー) インデックス ツリー内の対応するリーフ ノードのクラスター化インデックス ページを直接検索します (インデックス ツリーのキャッシュされた非リーフ ノードがない場合は、インデックス ツリーのキャッシュされた非リーフ ノードがない場合は、 mysql8.0 は、メモリ内の bp に現在キャッシュされている各インデックスのインデックス ページ数を表示するテーブルを提供します)
    2. 対応するクラスター化インデックス ページを見つけた後、二分法によってページ内の対応する行を見つけます。
    3. 行を変更し、影響を受ける行を同時に計算します (データが変更された行のみを計算します)
    フェーズ 2: セカンダリ インデックスを再度更新します (フィールド x にインデックスが付けられます)
    1. まず、メモリ内のセカンダリ インデックス ツリー、つまりセカンダリ インデックス ページを通じて、対応するリーフ ノードを見つけようとします。
    2. 対応するセカンダリ インデックス ページを見つけた後、二分法でページ内の対応する行を見つけます。
    3. インデックス ページがキャッシュに見つからない場合は、ディスクに移動して、対応するインデックス ページをバッファ プールに読み取る必要があります。
    4. メモリ内のインデックスを更新します。B+ ツリーのノード値を変更するには、バランスを維持するためにツリー全体を左右に選択する必要がある場合があることに注意してください。
    フェーズ 3: 最後のブラッシング
    1. バッファー プールに付属のメカニズムを使用して、クラスター化インデックスのダーティ ページとセカンダリ インデックスのダーティ ページをフラッシュします。

このロジックは問題ないように思えますが、最善の解決策ではありません。なぜ?
多数のランダム書き込み (書き込み数が多く、読み取り数が少ない) のシナリオの場合、上記のスキームでは大量のディスク ランダム I/O 読み取り操作 (ステップ 6) が発生し、インデックス ツリーによるオーバーヘッドが発生します。メンテナンスが難しく、同時書き込みのパフォーマンスは高くありません。

インデックス ページがバッファ プールで見つからない場合は、別のスペース (変更バッファ) を使用して、セカンダリ インデックスの変更を保存します (インデックス自体は保存されないことに注意してください)。具体的な保存データの種類は次のとおりです。

  • 入れる
  • 削除マーキング
  • physical delete(purge操作)

え、更新ないの?実際、上記にもありますが、更新 SQL ステートメントの実際の操作は、最初に挿入し、次に削除します。インプレース変更ではなく削除マーキングを使用する理由は何ですか? この説明には、説明するための関連情報がまだ見つかりません。

CB のインデックス変更キャッシュは、所定のタイミングでバッファプールに同期され、非同期でディスクに書き込まれます。いつ?

  • ステートメント select...where x=? が実行されるときなど、インデックスのこの部分を読み取る必要があるとき
  • データベースがアイドル状態のとき
  • データベースが正常に起動およびシャットダウンするとき
  • REDOログがいっぱいになった場合

更新する行とセカンダリ インデックスが多数ある場合、CB のマージに数時間かかる場合があり、ディスク I/O が増加し、ディスクに依存するクエリ リクエストに影響します。マージ オペレーションはトランザクション時にも送信されます。 commit 、mysql の起動時またはシャットダウン時に。

パージ操作は、mysql がアイドル状態またはゆっくりとシャットダウンしているときに実行され、セカンダリ インデックス ページを順番にディスクに書き込みます。これは、更新のたびにディスクをフラッシュするよりもはるかに効率的です。

mysql データの多くの場所でパージの説明が見られますが、これは通常、mysql がデータ/ログのフラッシュ、バッファの切り捨てなどの操作を行うために別のスレッドを開始することを意味します。このとき、このスレッドは一般にパージ スレッドと呼ばれます。


  • 主キー インデックスまたは一意のインデックスの更新にCB が使用されなくなるのはどのような場合ですか。これらはすべて一意であるため、最初にキャッシュに書き込むことはできません。

  • CB の利点 CB は
    ディスクの読み取りと書き込みを削減するため、I/O 集中型のワークロードにとって最も価値があります。たとえば、一括挿入などの負荷の高い DML 操作を行うアプリケーションは CB の恩恵を受けます。ただし、セカンダリ インデックス データが少ない場合は、CB を減らすか無効にして、バッファ プール用にさらに多くのスペースを解放する必要があります。

  • CB のスペースは
    メモリ内で占有され、CB はバッファ プール スペースの一部を占有します。ディスクでは、CB はシステム テーブル スペースに存在します (はい、CB の B+ ツリーもディスク上に配置されます)。後者は、mysql が閉じられたときにインデックスが変更される物理インデックスでもあり、キャッシュ領域、つまり ibdata1 です。

  • CB構成

    • このパラメーターはinnodb_change_buffering、CB にキャッシュされるデータ型 (実際には SQL 型) を制御します。デフォルトはallオプションnone、、、、、です(パージは物理的な削除の操作を指します) insertsdeleteschangespurges
    • このパラメータはinnodb_change_buffer_max_sizeCB の使用可能なスペースを制御します。タイプは int で、バッファ プール スペースの占有率はデフォルトで 25、最大値は 50 です。
  • CBステータスの監視

mysql> SHOW ENGINE INNODB STATUS\G
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 4425293, used cells 32, node heap has 1 buffer(s)
13577.57 hash searches/s, 202.47 non-hash searches/s
  • テーブルからinformation_schema.innodb_metricsより詳細なデータを取得する
  • CB のパフォーマンスへの影響はテーブル統計を通じて確認information_schema.innodb_buffer_pageできます。具体的な SQL については、公式 Web サイトを参照してください。

1.4 アダプティブ ハッシュ インデックス (アダプティブ ハッシュ インデックス)

AHI と略され、具体的には、頻繁にアクセスされるインデックス ページ (ホット ページ) のハッシュ インデックスを構築します。これはインデックスのインデックスと言えます。ホット ページの検索プロセスが高速化されます
従来、B+tree による検索には 2 ~ 3 回の I/O が必要でしたが、ランダム インデックス ページはハッシュ インデックスを通じて直接検索できるため、ツリー検索が不要になります。

次のようないくつかの特徴があります。

  • = および IN 演算子のみを含む等価クエリなどのシナリオの使用を制限する
  • ハッシュ インデックスのキー値は、where 条件のインデックス フィールドです。複合インデックス フィールドの場合は、一部のフィールドのみを含めることができます。
  • 一部のシナリオでは、アダプティブ ハッシュ インデックスへのアクセスが、同時結合クエリの同時実行など、深刻なロック競合を引き起こすことがあります。
  • MySQL は介入なしで自動的に調整します

バージョン 5.7 以降では、AHI はセグメント化されたロックを使用して、同時実行性の高いシナリオでのロックの競合を減らし、パフォーマンスを向上させます。公式ドキュメントでは分割されていると記載されていますが、実際には同じ意味です。これはパラメータによって制御されinnodb_adaptive_hash_index_parts、デフォルトは 8、最高値は 512 です。

関連する構成を表示しますshow variables like '%hash_index%'
ここに画像の説明を挿入
閉じるset global innodb_adaptive_hash_index='off'

AHI の使用状況を監視するにはshow engine innodb status:

-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 557, seg size 559, 304617 merges
merged operations:
 insert 688133, delete mark 206010, delete 11078
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 276671, node heap has 59 buffer(s)
330.50 hash searches/s, 114.83 non-hash searches/s

この部分の情報はキーワードで検索するとADAPTIVE見つかります。最後の 2 行はハッシュ インデックスの情報を示し、非ハッシュは非ハッシュ インデックスのクエリの数を示します。ハッシュ クエリより大きい場合は、現在のシステムがAHI は使用されないため、AHI をオフにする必要があります。

show engine innodb status出力の一部で、AHI のロック競合を監視しますSEMAPHORES

----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 2134292
OS WAIT ARRAY INFO: signal count 1847913
Mutex spin waits 3840490, rounds 50352831, OS waits 1188131
RW-shared spins 1279542, rounds 34353371, OS waits 845762
RW-excl spins 170132, rounds 3635212, OS waits 73050
Spin rounds per wait: 13.11 mutex, 26.85 RW-shared, 21.37 RW-excl

btr0sea.c で作成された rw-latch を待機しているスレッドが多数ある場合 (btr0sea.c で作成された rw-latch を待機しているスレッドが多数あるが、それが出力に表示されない場合)、次のことが必要になる場合があります。値を増やしますinnodb_adaptive_hash_index_parts

1.4 ログバッファ

メモリー・バッファー内の REDO ログとして使用されるメモリー領域です。ログ・バッファーのサイズを増やすと、トランザクションの同時実行パフォーマンスが向上し、ディスク I/O が削減されます (ログ・バッファーが十分でない場合、REDO ログはフラッシュされます)。さらに、ログ バッファも定期的にフラッシュされます。

関連する調整可能な変数:

  • innodb_log_buffer_size、デフォルトは16MB
  • innodb_flush_log_at_trx_commit、ログ バッファーの内容をディスクに書き込んでフラッシュする方法を制御します。具体的な詳細には REDO ログ部分が関係するため、今は詳しく説明しません。
  • innodb_flush_log_at_timeout、ログの更新頻度を制御します。

さらに、REDO ログの物理ファイル サイズを調整する変数がいくつかあります。

  • innodb_log_file_size単一のREDOログ・ファイルのサイズを制御します。デフォルトは48MB、最大値は512GBです。ファイル名は、ib_logfile0およびib_logfile1です。これら2つのファイルはループで書き込まれます。値が大きいほど、より多くのREDOログを書き込むことができます。データ ページの I/O 時間、および mysql 起動時の回復時間の増加により、1G REDO ログ ファイルの回復に必要な参照時間は 5 分になります。
  • innodb_log_files_in_groupREDO ログ ファイルの数を制御します (デフォルトは 2)
  • innodb_log_group_home_dirREDO ログの場所、デフォルトのデータ ディレクトリを制御する

おすすめ

転載: blog.csdn.net/sc_lilei/article/details/119416787