InnoDBが行カウントを保存しないのはなぜですか?


19

InnoDBをエンジンとして使用するテーブルではSELECT COUNT(*) FROM mytable、特にテーブルが大きくなり、そのクエリの実行中に一定の行の挿入/削除がある場合、クエリは非常に不正確で非常に遅いことを誰もが知っています。

私が理解したように、InnoDBは行数を内部変数に保存しません。これがこの問題の原因です。

私の質問は:なぜこれがそうなのか?そのような情報を保存するのは難しいでしょうか?これは非常に多くの状況で知っておくべき重要な情報です。そのような内部カウントが実装されるかどうか私が見る唯一の難しさは、トランザクションが関与している場合です:トランザクションがコミットされていない場合、それによって挿入された行をカウントしますか?

PS:私はDBの専門家ではなく、MySQLを単純な趣味として持っているだけの人です。だから、私がばかげたことを尋ねただけなら、過度に批判的にならないでください:D。


6
遅い、はい。正確ではありません。正確な結果が得られるため、時間がかかります。200M行のテーブルと、おそらく同じテーブルに挿入/削除する他の多くのトランザクション、おそらく1秒あたり多くの行がある場合、別の質問は「正確な数が必要ですか?」です。
ypercubeᵀᴹ

@ypercube phpmyadminで行カウント値が非常にオフになっていることが何度かありました。さらに、「正確でない可能性がある」などのコメントがあります。
ラドゥマーゼア

1
@RaduMurzea phpMyAdminは、知っている速度上の理由から、InnoDBテーブルのテーブルカウントを計算する代替方法を使用します。これは、あなたが言及した不正確さが作用するところです。実際のSELECT COUNT(*) FROM ...クエリは正確です。必要に応じて、速度を犠牲にして常に正確な行数を使用するようにphpMyAdminを構成できます。さらに詳しい情報:stackoverflow.com/questions/11926259/...
DOOManiac

回答:


9

@RemusRusanuに同意します(彼の回答に対して+1)

SELECT COUNT(*) FROM mydb.mytableInnoDBでは、トランザクションストレージエンジンのように動作します。MyISAMと比較してください。

MyISAM

mydb.mytableがMyISAMテーブルの場合、起動SELECT COUNT(*) FROM mydb.mytable;はの実行と同じSELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';です。これにより、MyISAMテーブルのヘッダー内の行カウントのクイックルックアップがトリガーされます。

InnoDB

場合mydb.mytableのInnoDBテーブルで、あなたは物事のホッジ-podgeが起こってもらいます。次を管理するMVCCが実行されています。

  • ib_logfile0 / ib_logfile1(ログのやり直し)
  • ibdata1
    • ログを元に戻す
    • ロールバック
    • データ辞書の変更
  • バッファプール管理
  • トランザクション分離(4種類)
    • 繰り返し可能な読み取り
    • コミットを読む
    • コミットされていない読み取り
    • シリアライズ可能

InnoDBにテーブルカウントを要求するには、これらの不吉なものをナビゲートする必要があります。実際、SELECT COUNT(*) from mydb.mytable反復可能な読み取りのみをカウントするのか、コミットされた読み取りとコミットされていない読み取りを含めるのかは、まったくわかりません。

innodb_stats_on_metadataを有効にすることで、物事を少し安定させることができます

innodb_stats_on_meta_dataのMySQLドキュメントによると

この変数が有効な場合(変数が作成される前のデフォルト)、InnoDBは、SHOW TABLE STATUSやSHOW INDEXなどのメタデータステートメント中、またはINFORMATION_SCHEMAテーブルTABLESまたはSTATISTICSへのアクセス中に統計を更新します。(これらの更新はANALYZE TABLEの場合と同様です。)無効にすると、InnoDBはこれらの操作中に統計を更新しません。この変数を無効にすると、多数のテーブルまたはインデックスがあるスキーマのアクセス速度が向上します。また、InnoDBテーブルを含むクエリの実行プランの安定性を向上させることもできます。

これを無効にすると、EXPLAINプランの設定に関してより安定したカウントが得られる場合と得られない場合があります。パフォーマンスにSELECT COUNT(*) from mydb.mytable良い影響、悪い影響、またはまったく影響しない可能性があります。試してみてください!!!


16

まず、変数に格納する「現在のカウント」などはありません。のようなクエリSELECT COUNT(*) FROM ...は、現在の分離レベルとすべての同時保留トランザクションの影響を受けます。分離レベルに応じて、クエリは保留中のコミットされていないトランザクションによって挿入または削除された行を表示するかどうかを確認できます。答える唯一の方法は、現在のトランザクションから見える行を数えることです。

カウント中に開始または終了する同時トランザクションのさらに厄介な主題にさえ触れなかったことに注意してください。ロールバックは言うまでもありません...


1
わかりましたので、それは分離レベルに依存しています、それは理にかなっています。しかし、それはまだ実装できます。
ラドゥムルゼア

@SoboLANすべきではない、できない理由はたくさんありますが、そのほとんどは上記にリストされています。トランザクションの開始ごとにテーブルごとのカウントのリストを維持して実装しますか(OracleのSCNがMySQLにあるものは何でも)。このようなカウントの管理は大きなオーバーヘッドになります-同じテーブルで大量のINSERT / DELETEをそれぞれ実行する100または1000の同時セッションを持つデータベースを考えてください。維持することは不可能です。
フィリ

これを実装することは非常に困難です。カウントはDBに永続化する必要があると考えてください。つまり、メタデータのどこかを意味し、このカウントは行を挿入または削除するすべてのトランザクションで維持する必要があります。そのメタデータをどのようにロックしますか?そして、どのようにロールバックを処理しますか?些細なことからはほど遠い。そして、結果は非常に狭いクエリのサブセットに使用できます。
レムスルサヌ

3
@JackDouglas興味深い。過去に私が見てきたことからCOUNT(*)、実際にはクエリはほとんど必要なく、通常は開発者の経験不足(行を選択する前に行を数えます!)または不適切なアプリ設計の結果です。
フィリ

1
@SoboLAN-いいえ、そうではありません。事前定義された時間間隔で何らかの統計テーブルを更新するサービスがあると、はるかに優れています。大規模なデータベースと複数の管理者がほとんどのテーブルをSELECT COUNT(*)でクエリすることを想像してくださいWHERE
NB

0

理論的には、InnoDBを使用して特定のテーブルの行数を正確にカウントすることは可能ですが、多くのロックが必要になり、パフォーマンスに悪影響を及ぼします。分離レベルによっても異なります。

MyISAMはすでにテーブルレベルのロックを行っているため、追加費用はかかりません。

COUNT(*)をかなり頻繁に使用しますが、テーブルの行カウントはほとんど必要ありません。通常、WHERE句が添付されています。小さい結果セットで効率的なインデックスを使用すると、十分に高速であることがわかります。

カウントが不正確であることに同意しません。カウントはデータのスナップショットを表し、常に正確であることがわかりました。

要するに、MySQLはこれをInnoDBに実装するのはあなた次第です。カウントを保存し、各クエリの後にインクリメント/デクリメントできます。ただし、より簡単な解決策は、おそらくMyISAMに切り替えることです。


2
それはだていないトランザクションシステム内の行の正確なカウントを維持することができます。なぜなら、アクティブなトランザクションと同数の異なる(そして正しい)行カウントがあるからです。
a_horse_with_no_name

5
ここでは-1を指定しましたが、「より簡単な解決策は、おそらくMyISAMに切り替えることです。」行数を取得するためだけにMyISAMに切り替えることはお勧めしません。
デレクダウニー

@a_horse_with_no_nameであるため、各トランザクションに「正しい」行数があることに同意します。私には可能なようです。
マーカスアダムス

1
@DTest、「単純に行数を取得する」と言ったことはありません。
マーカスアダムス

@a_horse_with_no_name、それは正しくないようです。トランザクションが正しくコミットされたときにのみ行数をカウントしているのは確かですか?
Pacerier
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.