web-dev-qa-db-ja.com

MySQL COUNT(*)パフォーマンス

15m行を超えるテーブルがあります。行の総数が必要です。そう:

SELECT COUNT(*) FROM thetable;

完了するまでに約50秒かかります。説明するとSelect tables optimized away。これは、インデックスを使用することによってのみ結果が見つかることを意味していると思いますが、それでもなぜそれほど時間がかかるのですか? id列のインデックスに関するいくつかの情報を次に示します(nullにすることはできません)。

インデックスタイプ:BTREE(クラスター)

カーディナリティ:14623100

ユニーク:はい

このクエリのパフォーマンスを向上させるにはどうすればよいですか?ありがとう。

注:データベースはMySQL 5.7.1で、InnoDBエンジンを使用しています。

編集:

ステートメントの作成:

CREATE TABLE `properties` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `address` varchar(255) DEFAULT NULL,
  `locality` varchar(50) DEFAULT NULL,
  `latitude` decimal(13,9) DEFAULT NULL,
  `longitude` decimal(13,9) DEFAULT NULL,
  `state` varchar(10) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  .....
  PRIMARY KEY (`id`),
  KEY `index_properties_on_address` (`address`),
  KEY `index_properties_on_latitude` (`latitude`),
  KEY `index_properties_on_longitude` (`longitude`),
  KEY `index_properties_on_state` (`state`),
  KEY `index_properties_on_created_at` (`created_at`),
  .....
) ENGINE=InnoDB AUTO_INCREMENT=28267712 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED;

注:一部の行を省略しました。44列あります。

プランの説明:

 + ---- + ------------- + ------- + ------------ + ---- -+ --------------- + ------ + --------- + ------ + ------ + ---------- + ------------------------------ + 
 | id | select_type |テーブル|パーティション|タイプ|可能性のあるキー|キー| key_len | ref |行|フィルター済み|追加| 
 + ---- + ------------- + ------- + ------------ +- ---- + --------------- + ------ + --------- + ------ + ----- -+ ---------- + ------------------------------ + 
 | 1 |シンプル| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |最適化されたテーブルを選択する| 
 + ---- + ------------- + ------- + ------------ + ------ + --------------- + ------ + --------- + ------ +- ---- + ---------- + ------------------------------ + 
7
lunr

Mysqlがデフォルトでトランザクション的に適切でなかった場合(デフォルトであったため、人々がInnoDBの代わりにmyISAMテーブルを定期的に使用した場合、または、まだ存在していないため、さらに過去にさかのぼります)フィルタリング句のない「SELECT * FROM some_table」 mySQLが他のデータベースエンジンではるかに高速であることについて、peopelが強打したクエリタイプの1つでした。

トランザクション上安全な環境では、一般的にデータベースエンジンはすべての行をチェックし、それが現在のセッションから見えるようにする必要があります(つまり、まだコミットされていない(またはコミットされていない)トランザクションの一部ではありません)。このセッションのアクティブなトランザクションの開始)または現在ロールバック中)-すべての行をチェックすると、テーブルスキャンまたは(存在する場合は)クラスタ化インデックススキャンを実行する必要があることを意味します。

アクティブなセッション/トランザクションごとにエンジンが各オブジェクトに表示される行数を追跡することは可能ですが、おそらくデザイナーはそうではありませんこれは余分な処理に値するものであると判断したので、一般的には考慮されていないと思います実用的です-対処するのにかなり複雑なロック要件があると想像できます他の操作のパフォーマンスに過度に悪影響を与える同時実行性があります。対象のテーブルの行数が記録されているテーブルを保持することでこれを自分で実装し、すべてのコードでその値を細心の注意を払って維持することができますが、これはかなり面倒であり、エラーが発生しやすくなる可能性がありますバグはカウントが時間の経過とともに真からドリフトすることを意味します(そして、おそらくアプリケーション層で潜在的なデッドロックソースやロックボトルネックを追加しているでしょう)。

行レベルのセキュリティが使用されている状況では、これがさらに複雑になります。現在のトランザクションに関して行/ページのステータスを確認する必要があるだけでなく、エンジンは現在のユーザーも再度確認する必要があり、セキュリティルールも同様です。動的この情報をキャッシュしておくことは、ジャストインケースで毎回スキャンを必要とするため実際的ではありません。行レベルのセキュリティは次のリリース( https://msdn.Microsoft.com/en-us/library/dn765131.aspx )でMS SQL Serverに追加されており、すでにpostgres( http://www.postgresql.org/docs/9.5/static/ddl-rowsecurity.html )、他のRDBMSでのステータスはわかりません。

6
David Spillett