2016-07-11 5 views
1

私は250万行で成長するかなり大きいMySQLデータベーステーブルを持っています。クエリを高速化するために、インデックスの1つに列を追加しました。インデックスを手動で設定すると、たとえばPHPMyAdminによって、カーディナリティが約1500になります。これは正しいと思われ、クエリは問題なく実行されます。MySQLテーブルインデックスカーディナリティ

問題は、(特にINSERTではなく、これに限定されない)いくつかの問合せが実行された後、その索引のカーディナリティが17または18に低下し、問合せが非常に遅く実行された後に発生します。時には、それが約1500に戻るか、PHPMyAdminを使って再度実行しなければならないことがあります。

このカーディナリティードロップが起こらないようにする方法はありますか?

CREATE TABLE IF NOT EXISTS `probe_results` (
    `probe_result_id` int(11) NOT NULL AUTO_INCREMENT, 
    `date` date NOT NULL, 
    `month` int(11) NOT NULL, 
    `year` int(11) NOT NULL, 
    `time` time NOT NULL, 
    `type` varchar(11) NOT NULL, 
    `probe_id` varchar(50) NOT NULL, 
    `status` varchar(11) NOT NULL, 
    `temp_1` decimal(11,0) NOT NULL, 
    `temp_2` decimal(11,0) NOT NULL, 
    `crc` varchar(11) NOT NULL, 
    `raw_data` text NOT NULL, 
    `txt_file` text NOT NULL, 
    PRIMARY KEY (`probe_result_id`), 
    KEY `probe_id` (`probe_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2527300 ; 

「probe_result_id」列が主キーで、probe_idが問題のインデックスを持つ列です。

例クエリ:あなたが落下するのを防止するための直接的な手段を持っていないので、

SELECT IF(b.reactive_total IS NULL, 0, b.reactive_total) AS reactive_total, a.* FROM (SELECT COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_testing_month = '7' AND asset_testing_results.asset_stopped = '0' AND asset_testing_results.asset_testing_completed = '0' THEN 1 END) AS due_total, (COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_stopped = '0' AND asset_testing_results.asset_testing_completed = '1' AND asset_testing_results.asset_testing_satisfactory = '1' AND asset_testing_results.asset_testing_actioned = '0' THEN 1 END)+(IF(probes_passed_total IS NULL, 0, probes_passed_total))) AS passed_total, (COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_stopped = '0' AND asset_testing_results.asset_testing_completed = '1' AND asset_testing_results.asset_testing_satisfactory = '0' AND asset_testing_results.asset_testing_actioned = '0' THEN 1 END)+(IF(probes_failed_total IS NULL, 0, probes_failed_total))) AS failed_total, COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_stopped = '0' AND asset_testing_results.asset_testing_completed = '1' AND asset_testing_results.asset_testing_actioned = '1' THEN 1 END) AS actioned_total, COUNT(CASE WHEN asset_testing_results.asset_testing_year = '2016' AND asset_testing_results.asset_testing_month < '7' AND asset_testing_results.asset_testing_completed = '0' AND asset_testing_results.asset_testing_satisfactory = '0' AND asset_testing_results.asset_stopped = '0' THEN 1 END) AS missed_total, site.site_key, site.site_name FROM site LEFT JOIN location ON location.site_key = site.site_key LEFT JOIN sub_location ON sub_location.location_key = location.location_key LEFT JOIN asset ON asset.sub_location_key = sub_location.sub_location_key AND asset.stopped = '0' LEFT JOIN asset_testing ON asset_testing.asset_type_key = asset.asset_type_key AND asset_testing.probe_assessed = '0' LEFT JOIN asset_testing_results ON asset_testing_results.asset_testing_key = asset_testing.asset_testing_key AND asset_testing_results.asset_key = asset.asset_key LEFT JOIN (SELECT site.site_key, COUNT(CASE WHEN p.probe_id IS NOT NULL AND p.asset_testing_key IS NOT NULL THEN 1 END) AS probes_passed_total, COUNT(CASE WHEN p.probe_id IS NOT NULL AND p.asset_testing_key IS NULL AND p.temp_1 IS NOT NULL THEN 1 END) AS probes_failed_total FROM assetsvs_probes LEFT JOIN (SELECT q.probe_id, q.month, q.year, IF(r.temp_1 IS NULL, q.temp_1, r.temp_1) as temp_1, r.asset_testing_key FROM (SELECT DISTINCT probe_results.probe_id, probe_results.month, probe_results.year, probe_results.temp_1 FROM probe_results LEFT JOIN assetsvs_probes ON assetsvs_probes.probe_id = probe_results.probe_id LEFT JOIN asset ON asset.asset_key = assetsvs_probes.asset_key LEFT JOIN sub_location ON sub_location.sub_location_key = asset.sub_location_key LEFT JOIN location ON location.location_key = sub_location.location_key LEFT JOIN site ON site.site_key = location.site_key WHERE site.client_key = '25')q LEFT JOIN (SELECT probe_results.month, probe_results.year, probe_results.probe_id, temp_1, asset_testing.asset_testing_key FROM probe_results LEFT JOIN assetsvs_probes ON assetsvs_probes.probe_id = probe_results.probe_id LEFT JOIN asset_testing ON asset_testing.asset_testing_key = assetsvs_probes.asset_testing_key LEFT JOIN asset ON asset.asset_key = assetsvs_probes.asset_key LEFT JOIN sub_location ON sub_location.sub_location_key = asset.sub_location_key LEFT JOIN location ON location.location_key = sub_location.location_key LEFT JOIN site ON site.site_key = location.site_key WHERE temp_1 != 'invalid' AND ((temp_1 >= test_min AND test_max = '') OR (temp_1 <= test_max AND test_min = '') OR (temp_1 >= test_min AND temp_1 <= test_max)) AND year = '2016' AND site.client_key = '25' GROUP BY probe_results.month, probe_results.year, probe_results.probe_id)r ON r.probe_id = q.probe_id AND r.month = q.month AND r.year = q.year WHERE q.year = '2016' GROUP BY probe_id, month, year) p ON p.probe_id = assetsvs_probes.probe_id LEFT JOIN asset_testing ON asset_testing.asset_testing_key = assetsvs_probes.asset_testing_key LEFT JOIN asset ON asset.asset_key = assetsvs_probes.asset_key LEFT JOIN sub_location ON sub_location.sub_location_key = asset.sub_location_key LEFT JOIN location ON location.location_key = sub_location.location_key LEFT JOIN site ON site.site_key = location.site_key GROUP BY site.site_key) probe_results ON probe_results.site_key = site.site_key WHERE site.client_key = '25' GROUP BY site.site_key)a LEFT JOIN (SELECT COUNT(CASE WHEN jobs.status = '3' THEN 1 END) AS reactive_total, site.site_key FROM jobs LEFT JOIN jobs_meta ON jobs_meta.job_id = jobs.job_id AND jobs_meta.meta_key = 'start_date' LEFT JOIN site ON site.site_key = jobs.site_key WHERE site.client_key = '25' AND jobs_meta.meta_value LIKE '%/2016 %' GROUP BY site.site_key)b ON b.site_key = a.site_key 

おかげで(他の統計情報と一緒に)

+0

も ​​'EXPLAIN'をあなたのクエリに使用しています。 – Martin

+0

テーブルには13の列があります。一意のID列に主キーがあり、次にこの問題を引き起こしていると思われるこの2番目の索引があります。 – Ian

+0

あなたの質問を編集して、テーブルの列のプロパティの 'CREATE TABLE 'リストを出力できますか?インデックスを表示するかどうかを指定しますか? – Martin

答えて

2

カーディナリティは、自動的にMySQLので算出して更新されます。

ただし、これを実行しにくくするか、動作を修正するために、いくつかの手順を実行することができます。

まず、analyze tableコマンドを実行すると、サポートされているすべてのテーブルエンジンのインデックス統計が更新されます。

innodbテーブルエンジンでは、MySQLはサンプリングの動作に影響する一連の構成設定を提供します。設定とその効果は、MySQLのマニュアルに記載されている:1または2になりますように小さな値•

主な設定はinnodb_stats_transient_sample_pagesです カーディナリティの不正確な見積りになります。

•innodb_stats_transient_sample_pagesの値を大きくすると、ディスクの読み込みがさらに多くなることがあります( )。 は、テーブルを開くのにかかる時間が大幅に減速したり、 がSHOW TABLE STATUSを実行したりする可能性があります(たとえば、100)。オプティマイザ•

は、インデックスの選択

の 異なる推定に基づいて非常に異なるクエリ・プランを選択する可能性があります。

myisamの場合、MySQL dosはこのようなさまざまな設定を提供しません。 myisam_stats_methodの設定については、the general index statistics documentation