【MySQL基礎】クエリと更新文の実行処理 01-02

たとえば、次のクエリ ステートメントを実行すると、次のようになります。

mysql> select * from T where ID=10;

MySQL の基本アーキテクチャの概略図

ここに画像の説明を挿入します

MySQL は、サーバー層ストレージ エンジン層の2 つの部分に分けることができます

サーバー層には、コネクタ、クエリ キャッシュ、アナライザー、オプティマイザー、エグゼキューターなどが含まれており、MySQL のコア サービス機能のほとんどと、すべての組み込み関数 (日付、時刻、数学関数、暗号化関数など) をカバーします。ストレージ エンジン全体 ストアド プロシージャ、トリガー、ビューなど、すべての機能がこの層に実装されます。

ストレージ エンジン層は、データの保存と取得を担当します。そのアーキテクチャ モデルはプラグインであり、InnoDB、MyISAM、Memory などの複数のストレージ エンジンをサポートします。現在最も一般的に使用されているストレージ エンジンは InnoDB で、MySQL 5.5.5 以降、デフォルトのストレージ エンジンとなっています。テーブルの作成時にcreate tableエンジン タイプを指定しない場合、デフォルトで InnoDB が使用されます。テーブルの作成にメモリ エンジンを使用することを指定するには、ステートメント内で , をcreate table使用します。engine=memory異なるストレージ エンジンは、コネクタからエグゼキュータまでの部分であるサーバー層を共有します。

コネクタ

コネクタは、クライアントとの接続の確立、権限の取得、接続の維持および管理を担当します。接続コマンドは通常次のように記述されます。

mysql -h$ip -P$port -u$user -p

connection コマンド内のは、mysqlサーバーとの接続を確立するために使用されるクライアント ツールです。クラシック TCP ハンドシェイクが完了すると、コネクタは ID の認証を開始します。今回は、入力したユーザー名とパスワードが使用されます。

  • ユーザー名またはパスワードが間違っている場合、「ユーザーのアクセスが拒否されました」エラーが報告され、クライアント プログラムは実行を終了します。
  • ユーザー名とパスワードの認証に合格すると、コネクタはアクセス許可テーブルをチェックして、ユーザーが持っているアクセス許可を見つけます。以降、この際のパーミッション判定ロジックは今回読み込んだパーミッションに依存します。

つまり、ユーザーが接続を正常に確立した後は、管理者アカウントを使用してユーザーの権限を変更しても、既存の接続の権限には影響しません。変更が完了すると、新しい接続のみが新しい権限設定を使用するようになります。

接続が完了した後、その後のアクションがない場合、接続はアイドル状態になります。これはshow processlistコマンドで確認できます。コマンド列に「Sleep」と表示されている行は、システム内にアイドル状態の接続があることを示します。
ここに画像の説明を挿入します
クライアントが非アクティブな状態が長時間続くと、コネクタは自動的に切断します。この時間はwait_timeoutパラメータによって制御され、デフォルト値は 8 時間です。切断後、クライアントが再度リクエストを送信すると、エラー通知を受け取ります: Lost connection to MySQL server during queryこの時点で続行したい場合は、再接続してからリクエストを実行する必要があります。

データベースでは、接続が長いとは、接続が成功した後、クライアントがリクエストを継続する場合、常に同じ接続が使用されることを意味します。短い接続とは、いくつかのクエリが実行された後に接続が切断され、次のクエリのために新しい接続が再確立されることを意味します。使用中に接続を確立するアクションを最小限に抑えること、つまり、長い接続を使用することをお勧めします。

長い接続をすべて使用した後、MySQL が占有するメモリが急速に増加することがありますが、これは、MySQL が実行中に一時的に使用するメモリが接続オブジェクトで管理されるためです。これらのリソースは、接続が切断されると解放されます。そのため、長時間の接続が蓄積すると、メモリを占有しすぎてシステムによって強制終了 (OOM) される可能性があり、現象から判断すると MySQL が異常再起動します

  1. 長時間の接続は定期的に切断してください一定期間使用した後、またはメモリを消費する大規模なクエリが実行されたとプログラムが判断した後、接続は切断され、クエリが必要になってから再接続されます。
  2. MySQL 5.7 以降を使用している場合は、比較的大規模な操作を実行した後に毎回実行するmysql_reset_connectionことで、接続リソースを再初期化できます。このプロセスでは再接続や権限の確認は必要ありませんが、接続は作成されたばかりの状態に復元されます。

クエリキャッシュ

接続が確立されたら、select ステートメントを実行できます。実行ロジックはクエリ キャッシュになります。

MySQL はクエリ リクエストを取得すると、まずクエリ キャッシュに移動して、このステートメントが以前に実行されたかどうかを確認します。以前に実行されたステートメントとその結果は、key-valueペアの形式でメモリに直接キャッシュされる場合があります。keyこれはクエリ文とvalueクエリの結果です。クエリがこのキャッシュ内で直接見つかる場合key、これはvalueクライアントに直接返されます。

ステートメントがクエリ キャッシュにない場合は、実行フェーズが続行されます。実行が完了すると、実行結果はクエリ キャッシュに保存されます。クエリがキャッシュにヒットした場合、MySQL は後続の複雑な操作を実行せずに結果を直接返すことができるため、非常に効率的です。

クエリ キャッシュのエラーは非常に頻繁に発生します。テーブルが更新されている限り、長期間のみ更新される静的テーブルがない限り、このテーブルのクエリ キャッシュはすべてクリアされます。MySQL は、この「オンデマンドで使用する」アプローチを提供します。デフォルトの SQL ステートメントにクエリ キャッシュが使用されないようにパラメータを設定できますクエリ キャッシュを使用することが確実なステートメントについては、次のステートメントのように明示的に指定できます。
query_cache_typeDEMANDSQL_CACHE

mysql> select SQL_CACHE * from T where ID=10

: MySQL バージョン 8.0 では、クエリ キャッシュ機能全体が直接削除されています。つまり、この機能は 8.0 から完全に欠落しています。

アナライザ

クエリ キャッシュがヒットしない場合は、ステートメントの実際の実行が開始されます。まず、MySQL は SQL ステートメントを解析する必要があります。

アナライザーは最初に「字句解析」を実行します。次に「文法解析」を行います。字句解析の結果に基づいて、構文アナライザーは、入力された SQL ステートメントが文法規則に基づいた MySQL 構文を満たすかどうかを判断します。ステートメントが間違っている場合は、You have an error in your SQL syntax「 」というエラー通知が表示されます。

オプティマイザ

実行を開始する前に、オプティマイザによって処理する必要があります

オプティマイザは、テーブル内に複数のインデックスがある場合、またはステートメントに **複数テーブルの関連付け (結合)** がある場合に、どのインデックスを使用するかを決定し、各テーブルの接続順序を決定します例えば:

mysql> select * from t1 join t2 using(ID)  where t1.c=10 and t2.d=20;
  • まずテーブル t1 からレコード c=10 の ID を取得し、次にその ID に基づいてそれをテーブル t2 に関連付け、その後 t2 の d が 20 に等しいかどうかを判断します。
  • 最初にテーブル t2 からレコード d=20 の ID を取り出し、次にその ID に基づいてそれを t1 に関連付け、その後 t1 の c が 10 に等しいかどうかを判断することもできます。

2 つの実行方法の論理的な結果は同じですが、実行効率は異なります。オプティマイザーの役割は、どちらのソリューションを使用するかを決定することです。オプティマイザ フェーズが完了すると、このステートメントの実行計画が決定され、エグゼキュータ フェーズに入ります。

アクチュエーター

実行を開始するときは、まずテーブル T でクエリを実行する権限があるかどうかを判断する必要があります。そうでない場合は、以下に示すように権限なしのエラーが返されます (クエリ キャッシュがヒットした場合、クエリ キャッシュがその結果、権限の検証が行われます。クエリは、オプティマイザが権限を検証する前にも呼び出されますprecheck)

mysql> select * from T where ID=10;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

権限がある場合は、テーブルを開いて実行を続行します。テーブルが開かれると、エグゼキューターはテーブルのエンジン定義に基づいてエンジンによって提供されるインターフェイスを使用します

たとえば、例のテーブル T では、ID にインデックスがない場合、エグゼキュータの実行フローは次のようになります。

  1. InnoDB エンジン インターフェイスを呼び出して、このテーブルの最初の行を取得し、ID が 10 であるかどうかを確認します。そうでない場合は、スキップします。そうである場合は、この行を結果セットに保存します
  2. エンジン インターフェイスを呼び出して「次の行」を取得し、テーブルの最後の行が取得されるまで同じ判断ロジックを繰り返します。
  3. エグゼキューターは、上記の走査プロセス中に条件を満たすすべての行で構成されるレコード セットを結果セットとしてクライアントに返します。

インデックスのあるテーブルの場合、実行ロジックは同様です。最初の呼び出しは「条件を満たす最初の行を取得する」インターフェイスであり、次にループして「条件を満たす次の行を取得する」インターフェイスです。これらのインターフェイスはエンジンで定義されています。

rows_examinedデータベースのスロー クエリ ログに、このステートメントの実行中にスキャンされた行数を示すフィールドが表示されますこの値は、エグゼキューターがエンジンを呼び出してデータ行を取得するたびに蓄積されます。一部のシナリオでは、エグゼキューターが 1 回呼び出され、エンジン内で複数の行がスキャンされるため、エンジンによってスキャンされた行の数と rows_examined はまったく同じではありません。

重要なログモジュール: REDO ログ

update ステートメントから始まるこのテーブルの作成ステートメントは次のとおりです。このテーブルには主キー ID と整数フィールド c があります。

mysql> create table T(ID int primary key, c int);

ID=2の行の値に1を足す場合

mysql> update T set c=c+1 where ID=2;

すべての更新操作をディスクに書き込む必要があり、更新前にディスクで対応するレコードを見つける必要がある場合、プロセス全体の IO コストと検索コストが非常に高くなります。この問題を解決するために、MySQL はホテルの店主のピンクのボードと同様のアイデアを使用して更新効率を向上させます。

ピンクのボード台帳の連携プロセス全体は、実際にはMySQL でよく言及されるWALテクノロジーであり、WALその正式名称は、Write-Ahead Loggingそのキーポイントは、最初にログを書き込み、次にディスクに書き込むこと、つまりピンクのボードに書き込むことです。最初に、忙しくないときに書き込みます

レコードを更新する必要がある場合、InnoDB エンジンはまずレコードをREDO ログ(ピンク色のボード) に書き込み、メモリを更新します。この時点で更新は完了します。同時に、InnoDB エンジンは適切なタイミングで操作記録をディスクに更新します。この更新は、店主が閉店後に行うのと同じように、システムが比較的アイドル状態のときに行われることがよくあります。

しかし、ある日、たくさんの信用口座がありピンクのボードがいっぱいになった場合、店主は自分がしていることを書き留め、ピンクのボードの信用記録の一部を台帳に更新し、その後これらを消去する必要があります。ピンクのボードからレコードを取得して、新しいアカウントを保持してスペースを確保する準備をします。InnoDB のREDO ログはサイズが固定されており、例えば 4 つのファイルを 1 セットとして構成し、各ファイルのサイズが 1GB である場合、この「ピンクのボードには合計 4GB の操作を記録できます。最初から書き始めて最後まで書き、その後最初に戻ってループで書きます。
ここに画像の説明を挿入します
write pos現在のレコードの位置で、書き込み中に逆方向に移動し、ファイル No.3 の最後まで書き込んだ後、ファイル No.0 の先頭に戻ります。checkpoint消去するのは現在位置であり、また前方に移動して循環するため、レコードを消去する前にデータファイルにレコードを更新する必要があります。

write poscheckpointの間には、新しい操作を記録するために使用できる「ピンクのボード」の空の部分があります。追いついた場合は、「ピンクのボード」がいっぱいで、現時点では新しい更新を実行できないことを意味します。先に進むには、まず停止していくつかのレコードを消去する必要がありますwrite poscheckpointcheckpoint

REDO ログを使用すると、InnoDB はデータベースが異常に再起動した場合でも、以前に送信されたレコードが失われないことを保証できます。この機能はクラッシュ セーフと呼ばれます。(クラッシュセーフ: 信用記録がピンクのボードに記録されているか台帳に記載されていれば、店主が突然数日間営業を停止するなど、後で忘れてしまっても、営業を再開した後に明らかにすることができます)台帳とピンクのボードのデータによる信用口座)

innodb_flush_log_at_trx_commitこのパラメータが に設定されている1場合、各トランザクションのREDO ログがディスクに直接保存されることを意味します。これにより、MySQL が異常に再起動した後にデータが失われることがなくなります。

重要なログモジュール: binlog

ピンクのボードREDO ログはInnoDB エンジンに固有のログであり、サーバー層にもbinlog (アーカイブ ログ)と呼ばれる独自のログがあります。

当初、MySQL には InnoDB エンジンはありませんでした。MySQL 独自のエンジンは MyISAM ですが、MyISAM にはクラッシュセーフ機能がなく、binlog ログはアーカイブにのみ使用できます。InnoDB は、他社によってプラグインの形で MySQL に導入されましたが、binlog のみに依存するとクラッシュ セーフ機能がないため、InnoDB は別のログ システム、つまり REDO ログを使用してクラッシュ セーフ機能を実現します。

これら 2 つのログには、次の 3 つの違いがあります。

  1. REDO ログはInnoDB エンジンに固有であり、 binlog はMySQL のサーバー層によって実装され、すべてのエンジンで使用できます。
  2. REDO ログ「特定のデータ ページにどのような変更が加えられたか」を記録する物理ログであり、 binlogは「ID を持つ行の c フィールドに 1 を追加する」など、ステートメントの元のロジックを記録する論理ログです。 =2" 。
  3. REDO ログはループで書き込まれ、スペースは常に使い果たされますが、binlog は追加で書き込むことができます「追加書き込み」とは、 binlogファイルが特定のサイズに達した後、次のファイルに切り替わり、前のログを上書きしないことを意味します。

この単純な更新ステートメントを実行するときのエグゼキューターと InnoDB エンジンの内部フロー。

  1. エグゼキューターはまずエンジンを調べてID=2この行を取得します。ID は主キーであり、エンジンはツリー検索を直接使用してこの行を見つけます。ID=2 の行が配置されているデータ ページがすでにメモリ内にある場合は、そのデータ ページが直接エグゼキュータに返されます。そうでない場合は、最初にディスクからメモリに読み込まれてから返される必要があります。
  2. エグゼキュータはエンジンによって与えられた行データを取得し、この値に 1 を加えます。たとえば、以前は N でしたが、現在は N+1 になり、新しいデータ行を取得して、エンジン インターフェイスを呼び出してこれを書き込みます。新しいデータ行。
  3. エンジンは、この新しいデータ行をメモリに更新し、更新操作をREDO ログに記録します。この時点で、REDO ログは 状態になりますprepare次に、実行が完了し、いつでもトランザクションを送信できることを実行者に通知します。
  4. エグゼキュータは、この操作のバイナリログを生成し、そのバイナリログをディスクに書き込みます。
  5. エグゼキューターはエンジンのコミット トランザクション インターフェイスを呼び出し、エンジンは書き込まれたばかりのREDO ログをcommit ( commit) 状態に変更し、更新が完了します。

update ステートメントの実行フローチャート。図中の明るいボックスはInnoDB 内で実行されることを示し、暗いボックスはexecutorで実行されることを示します。
ここに画像の説明を挿入します
最後の 3 つのステップは少し「複雑」に見えます。REDOログprepareの書き込みは、と の2 つのステップに分かれていますcommit。これが「2 フェーズ コミット」です。

sync_binlogこのパラメータが に設定されている1場合、各トランザクションのバイナリログがディスクに保存されることを意味します。これにより、 MySQL が異常に再起動した後にバイナリログが失われることがなくなります。

更新時には、REDO ログと binlog が 2 段階で送信されます。

2 フェーズ コミットは、2 つのログ間のロジックを一貫させることです。

指定した秒に復元する必要がある場合 (たとえば、ある日の午後 2 時)、テーブルが正午に誤って削除されたことに気づき、データを取得する必要がある場合は、次のように実行できます。

  • まず、最新の完全バックアップを見つけます。運が良ければ、昨夜のバックアップである可能性があります。このバックアップから一時データベースに復元します。
  • 次に、バックアップ時点から開始して、バックアップバイナリログを順番に取り出し、正午にテーブルが誤って削除される直前まで再生します。

ログに「2 フェーズ コミット」が必要な理由:

REDO ログbinlog は2 つの独立したロジックであるため、2 段階の送信を使用しない場合は、最初にREDO ログを書き込んでからbinlog を書き込むか、その逆の順序を採用する必要があります。何が問題になるでしょうか。ID=2 の現在の行のフィールド c の値が 0 であると仮定し、更新ステートメントの実行中、最初のログが書き込まれた後、2 番目のログが書き込まれる前にクラッシュが発生したとします。

  1. 最初に REDO ログを書き込み、次に binlog を書き込みますREDO ログが書き込まれたとき、バイナリログが書き込まれる前に、MySQL プロセスが異常に再起動したとします。前に述べたように、REDO ログが書き込まれた後は、システムがクラッシュしてもデータを復元できるため、回復後のこの行の c の値は 1 になります。
    ただし、バイナリログが終了する前にクラッシュしたため、この時点ではこのステートメントはバイナリログに記録されませんでした。したがって、後でログをバックアップするときに、このステートメントは保存されたバイナリログには含まれません。
    このバイナリ ログを使用して一時ライブラリを復元する必要がある場合、このステートメントのバイナリ ログが失われているため、今回は一時ライブラリは更新されません。復元された行の c の値は 0 であり、 の値とは異なります。オリジナルのライブラリ。

  2. 最初に binlog を書き込み、次に redo log を書き込みますbinlog の書き込み後にクラッシュが発生した場合、REDO ログはまだ書き込まれていないため、クラッシュ回復後のトランザクションは無効になるため、この行の c の値は 0 になります。ただし、binlog には「c を 0 から 1 に変更する」というログが記録されています。そのため、後で binlog を使用して復元すると、トランザクションが 1 つ増えてしまい、復元された行の c の値は 1 となり、元のデータベースの値とは異なります。

「 2 フェーズ コミット」が使用されていない場合データベースの状態が、ログを使用して復元されたライブラリの状態と一致しない可能性があることがわかります

容量を拡張する必要がある場合、つまりシステムの読み取り容量を増やすためにさらにバックアップ データベースを構築する必要がある場合、現在では完全バックアップを使用し、binlog を適用してこれを実現するのが一般的です。マスター データベースとスレーブ データベース間の不一致により、オンラインの問題が発生します

簡単に言えば、REDO ログbinlog の両方を使用してトランザクションのコミット ステータスを表すことができ、2 フェーズ コミットは2 つの状態の論理的な一貫性を保つことです。

おすすめ

転載: blog.csdn.net/qq_44033208/article/details/132736566