私はあなたがパフォーマンスの最適化を照会言うならば、通常の状況下では、あなたが最初のクエリを考えることは、大量のデータを返す必要があり、いくつかの複雑な文を考えます。しかし、いくつかのケースでは、「チェックライン」、特に遅いを実行します。今日、私はこの現象を表示され、どのような状況下で見るためにあなたにこの興味深いトピックをお話したいです。
MySQLデータベース自体が圧力がたくさんある場合は、データベースサーバーのCPU使用率につながるが高いか(IO使用状況)ioutilであることに留意すべきである。この場合、すべての文がスローダウンする可能性があり、非常に高く、所属していません私たちは、今日の範囲を議論します。
説明を容易にするため、私は、今日の問題を説明するために、このテーブルに基づいて、テーブルを構築しました。この表は、二つのフィールドID及びCを有しており、私はそれに100,000行を挿入します。
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=100000) do
insert into t values(i,i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();
复制代码
次に、私はいくつかの異なるシナリオを使用することになり、例えば、前の記事のいくつかは、我々はあなたがそれをテストするために、透けて見えることができるかどうか見て、知識を紹介しています。
最初のカテゴリ:クエリが長時間戻りません
1、テーブルtで次のSQL文を実行します。
mysql> select * from t where id=1;
复制代码
長い時間のためのクエリ結果が返されません。
図1のクエリは、長い時間のために戻りません
一般に、このような状況に遭遇し、それは大きな確率テーブルtがロックされています。理由の次の分析は、通常、現在の文を述べるかを確認するには、show PROCESSLISTコマンドで最初に行きました。
その後、我々は、各状態のため、再現、そしてどのように対処する方法、その原因を分析します。
MDLロックなど
図2に示すように、メタデータ・ロック・テーブルの概略図を待って表示PROCESSLISTを表示するコマンドを使用することです。
図2は状態を示すテーブルのメタデータロックの待機します
表示されます。この状態は今トンのMDLの書き込みロックによって保持されている要求またはテーブルのスレッドは、select文がブロックされていることを意味しています。
第六の記事では、「グローバルおよびテーブルのロックは:非常に多く妨げるがあるかのテーブルにフィールドを追加しますか?「私は、再現性のある方法を生きることを紹介します。しかし、再生処理がMySQLの5.6バージョンに基づいていることに留意すべきです。MySQLの5.7リリースでは、MDLロック戦略を変更するので、シーンを再現することはできません。
しかし、MySQLの5.7バージョンこのシーンを再現するには、それはまた、非常に簡単です。図3に示すように、私は、単純な再生ステップを与えます。
テーブルのメタデータが5.7をロックを待って、図3のMySQLの再生ステップ
セッションは、T MDLテーブルロックテーブルコマンド、クエリおよびMDLのセッションBが保持している書き込みロックは読み取りロックを取得する必要があります。そのため、セッションBは、待機状態に入ります。
このような問題に対処するための方法は、それを殺す、その後、MDLの書き込みロックを保持している人を見つけることです。
結果の内部はPROCESSLISTを表示するので、コマンド列セッションAを見つけることは非常に簡単で、その結果、「スリープ」です。しかし、その後performance_schemaのSYSおよびシステム・ライブラリと、はるかに便利。(約10%の性能低下と比較してのオフが設定され、開始されたMySQLの上performance_schema =を設定する必要があります)
このテーブルを照会することによってSys.schema_table_lock_waitsは、我々はこの切断は注文を殺すことができ、直接閉塞の原因となるプロセスIDを見つけることができます。
4プラステーブルロックは、スレッドIDを押収しました
だからフラッシュ
次に、私はあなたをブロックする状況の別のチェックを与えるでしょう。
私はテーブルtによ、次のSQL文を実行します。
mysql> select * from information_schema.processlist where id=1;
复制代码
ここで、私は秘密したいと思います。
あなたは図5で見ることができます。私はあなたが、なぜこれが想像することができ、このスレッドの状態は、テーブルのフラッシュを待ってチェックアウト。
図の模式図。5テーブルフラッシュ状態を待っ
このステータスは現在、テーブル上のスレッドはトンにフラッシュ操作を行うことを約あったがあることを意味します。一般的に、使用状況をフラッシュする内部行うためのMySQLのテーブルには、次の2つがあります。
flush tables t with read lock;
flush tables with read lock;
复制代码
フラッシュの文はどちらも、指定したテーブルtあれば、その後、唯一のテーブルに近いトンを表し、あなたはMySQLで開いているすべてのテーブルを閉じ、特定のテーブル名を指定しない場合。
彼らは別のスレッドによってブロックされない限り、通常、これら2つのステートメントは、非常に迅速に実行します。
状況は、テーブルを待っている表示される場合がありますので、フラッシュの状態は次のとおりです。フラッシュのテーブルが別のステートメントによってブロックされたコマンドがあり、それは私たちのselect文をブロックしました。
今、私たちは、この場合の再生を見てみましょう手順再現し、図6に示すように:
図6は、ステップを再現テーブルのフラッシュを待っています
セッションAでは、私は意図的に、各行の睡眠(1)に1回呼び出さので、デフォルトでは、この文は、テーブルtがセッションで「開く」となっていた時に10万秒を、実行します。その後、フラッシュテーブルセッションBコマンドのトンと、テーブルtをオフに行くが、それはクエリセッションAのもう一方の端にある必要があります このように、再び照会するため、セッションC、それはフラッシュコマンドがブロックされます。
図7は、現在のステップショーPROCESSLIST複合体の結果です。この場合の調査は非常に簡単です、あなたは確かにそれを行う方法を知って、このショーのPROCESSLISTの結果を参照してください。
結果はPROCESSLIST図を示した。7のテーブルのフラッシュを待っています
行ロックなど
さて、テーブルレベルのロックテストの後、我々は最終的にエンジン内でselect文に来ました。
mysql> select * from t where id=1 lock in share mode;
复制代码
この文上記の使い方あなたが最後に、私たちの最初の8」の取引に精通している、単離された単離されたまたはではないでしょうか?「記事で述べたように、現在の読書について説明します。
アクセスID = 1今回は、トランザクションレコード、このラインに書き込みロックを保持してすでに存在する場合、読み込みロックを追加するには、このレコードをするので、私たちは、文がブロックされます選択します。
次のように再現して生活する手順:
図8行繰り返しロック
9行ロックシーンを表示PROCESSLIST
もちろん、セッションAは、提出していない、トランザクション、所持の書き込みロックを開始したセッションBの原因がブロックされています。
問題は解析することは困難ではありませんが、質問は書き込みロックを占有する人を見つける方法です。あなたは、MySQL 5.7のバージョンを使用している場合は、sys.innodb_lock_waitsテーブルで見つけることができます。
クエリ方法は次のとおりです。
mysql> select * from t sys.innodb_lock_waits where locked_table=`'test'.'t'`\G
复制代码
図Sys.innodb_lock_waitsロックチェックOK 10〜
あなたは、4つのスレッドが犯人の閉塞によって引き起こされ、この情報は非常に広く、見ることができます。そして、この方法は、QUERY 4を殺すか4をKILLで犯人を取り除きます。
しかし、「KILLのQUERY 4」が表示されないはずです。このコマンドは、ステートメント第4号、現在実行中のスレッドを停止することを示しており、この方法は役に立たない実際です。所有行ロックが更新ステートメントであるため、トランザクションがローID = 1のロックを削除できないように、このステートメントは、今実行KILLクエリが完了する前に実行されました。
実際には、4を有効にするにKILL、それが直接言うの接続を切断することです。ここでは、それらが切断された接続を暗黙のロジックであって、自動的にねじ接続内部で実行されているロール、行番号= 1のロックを解放します。
第二のカテゴリー:スロークエリ
「ロック」数多くの手紙の後、私たちは遅いクエリのいくつかの例を見てみましょう。
あなたは理由のSQLステートメントを初めて目を知っている必要があります。
mysql> select * from t where c=50000 limit 1;
复制代码
フィールドc上のインデックスがないので、このステートメントは、IDのみ主キーの順序スキャンを行って、そのため50,000行をスキャンする必要がありますすることができます。
確認として、あなたはスロークエリログを見ることができます。スローログのためにここに記録されたすべてのステートメントは、私が最初に設定long_query_time = 0、スロークエリログの時間しきい値が0に設定されているを接続した後に実行することに注意してください。
図11スローログ全表走査線50000
Rows_examinedディスプレイは、50,000行をスキャンします。あなたはありません非常に遅いうん、11.5ミリ秒を返して、我々は一般的に遅いクエリを考えられているオンラインつ以上の秒を設定されている、と言うかもしれません。しかし、あなたは覚えている:悪いクエリは必ずしも遅いクエリではありません。私たちの例だけで100,000行、データボリュームアップがあり、その後、実行時間が上がる線形です。
走査線の数、低速のゆえ実装は、これはよく理解されています。
しかし、その後、私たちは、走査線に見えますが、実装は非常に遅い文です。
図12は、この例では遅いログです。あなたが見ることができ、文が実行されます
mysql> select * from t where id=1;
复制代码
走査線の数は1ですが、実行時間は800ミリ秒の限りですが。
12走査線が、実装が非常に遅いです
それは、これらの時間をどこに費やされていることを不思議ではないですか?
私は少しをドロップダウンスローログのスクリーンショットに下る場合は、次の文を参照してくださいすることができ、共有モードでトンどこのid = 1錠から*を選択し、走査線を行うときには1行で、実行時間は0.2ミリ秒です。
図13プラススローログの共有モードでロック
より不思議ではないように見えますか?これは、共有モードロックでロックも理にかなって、時間がああ長い魚でなければなりません。
一部の学生は、すでに答えを持っていることがあります。あなたが応答しない場合、私はあなたのメッセージを与えるだろう、14は、これら二つの文の出力の実装の結果です。
二つの文の出力14
共有モードステートメントのロック付きC = 1でクエリ結果の最初の文は、C = 1000001を返します。より多くの学生がなぜ知っている必要があり、ここを参照してください。それでも分からない場合は、その後、心配しないでください。私が再現する手順を説明し、あなたに話をして、その理由を分析する必要があります。
図における再生ステップ15。
あなたが見る、セッション一貫したスナップショットコマンドを使用して、開始トランザクションとの最初のトランザクションを開始し、その後、セッションBは、更新ステートメントを開始しました。
どのような状態で、百万回のステートメントを更新し、ID = 1つのこの行を実行した後、セッションBの後?あなたは、図16に答えを見つけることができます。
16 ID = 1の図の状態データ
セッションBが百万回の更新を終了し、(ログを元に戻す)ログをロールバックするには、1万ドルを生成しました。
SQL文の共有モードでロックすると、現在の測定値は、したがって、非常に速く、この読み1000001の直接の結果である、とid = 1トンから*この文を選択し、一貫性を読んで、1000001から開始する必要があります万回の後に行わ実装アンドゥログ、続く、それは1件の結果を返します。
注、実際に記録をログ元に戻す、このような操作ロジック「2への3に」、「2 1を変更する」で、塗装マイナス目的は、図1を容易にすることです。
概要
今日は私が単純なテーブルの上にあなたを与えるで、「チェックライン」を行うロックと遅い実装例ように見えることがあります。それはテーブルロック、行ロックの概念を伴い、一貫性をお読みください。
実際の使用では、より複雑になるシーンを打ちました。しかし、ほとんど同じ、あなたは私が見つけ、問題を解決するために、資料に記載の測位方法に従うことができます。
最後に、私は今あなたに質問を残しました。
ロックされたとき、私たちは読み、例えば、この文を使用して、Tから選択*どこ共有モードのid = 1錠。読み取りロックは、この行にのみ適用されるように、インデックスIDため、行番号= 1に直接配置することができます。
しかし、それは、次のSQL文であれば、
begin;
select * from t where c=5 for update;
commit;
复制代码
一連の文は、それをロックする方法ですか?プラスロックはときに解放されますか?
私は記事の最後に次の参照で私の答えを与える、コメント欄に書かれて、あなたのアイデアや検証方法を置くことができます。聴いてくれてありがとう、あなたは一緒に読むためにもっとたくさんの友達にこの共有を送るために歓迎されています。
時間の問題について
前回の記事で、私はあなたが、あなたが前に遭遇したとの記事で同様のシーンを共有できることを願って、疑問を残します。
面白いシーンは、それは言って価値があります。私は彼の質問の書き換えをした、次のようにテーブルの構造は次のとおりです。
mysql> CREATE TABLE `table_a` (
`id` int(11) NOT NULL,
`b` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `b` (`b`)
) ENGINE=InnoDB;
复制代码
そこ今そのテーブルを仮定する、bの値であり、データを10万行を含むデータの一万行、「1234567890」は、ステートメントが実行されると仮定すると、ある今読み取り。
mysql> select * from table_a where b='1234567890abcd';
复制代码
このとき、MySQLはそれを強制する方法でしょうか?
理想的には、MySQLはフィールドbはVARCHAR(10)で定義され、それを空に返す必要があります参照してください。残念ながら、MySQLはしませんでした。
それともそれは「1234567890abcd」が一致しないインデックス、確かにすばやくインデックスの値がツリーBでないかを決定することはできませんを取得し、またすぐに空の結果を返すことができます。
しかし、実際には、MySQLがそうされていません。
プロセスはこれです、SQL文が非常に遅い実行します:
- 実行エンジンに渡されると、文字の切り捨てを行います。ラインの長さを規定するエンジンのみ10、カットのように最初の10バイトであるので、それは何試合「1234567890」です。
- そのようなデータ10万行の条件を満足します。
- 選択*ので、そのテーブルに戻っ100,000ください。
- しかし、各テーブルの後に戻ってサーバに行全体を識別すること層ではなく、B「1234567890abcd」の値を決定します。
- 結果は空です。
この例では、私たちの記事の内容に良い補完するものです。実装プロセスは機能を介して動作が、最終的に結果を取得し、サーバー層や判断を行うことかもしれないが。
ます。https://juejin.im/post/5d0368035188257a6b40e4c9で再現