0

継承したいくつかのデータベース/クエリで問題が発生しています。これは、いくつかの大規模なデータセットとそれらに対して行われているレポート用です。

私は微調整と調整を試みて、いくつかの改善を得ようとしています。

何が起こっているのかというと、MySQL がどのインデックスを使用するかをどのように決定しているかについて、私は 100% 明確ではありません。

以下にリストされている最初のクエリが、クエリ 2 で使用されるインデックスを使用していないのはなぜですか。クエリ 2 では、クエリ エンジンが実行する必要があると想定していることを実行しています。小さなテーブルを取得し、適切な値を取得してから適用します。より大きなテーブルを検索し、適切なインデックスを利用します。

ここで何が間違っていますか?というか、外部キー、インデックス、および結合がここでどのように機能するかについて、私は何を誤解していますか :)

ここに2つの関連するテーブルがあります

表 1
~ 450 行

CREATE TABLE `client_accounts_dim` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `client_id` int(10) unsigned NOT NULL,
 `service_provider_id` int(10) unsigned NOT NULL,
 `account_number` varchar(45) NOT NULL,
 `label` varchar(128) DEFAULT NULL,
 `service_provider_name` varchar(45) NOT NULL,
 `client_name` varchar(45) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `client_id` (`client_id`,`account_number`)
) ENGINE=InnoDB;

表 2
~11,000,000 行

CREATE TABLE `invoices_fact` (
 `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `invoice_number` varchar(45) NOT NULL COMMENT '    ',
 ...
 ...
 `tracking_number` varchar(45) DEFAULT NULL,
 `division_id` int(11) DEFAULT NULL,
 `client_accounts_dim_id` int(10) unsigned NOT NULL,
 `invoice_date_dim_id` bigint(20) DEFAULT NULL,
 `shipment_date_dim_id` bigint(20) NOT NULL,
 `received_date_dim_id` bigint(20) NOT NULL,
 PRIMARY KEY (`id`),
 KEY `fk_invoice_details_client_accounts_dim1_idx` (`client_accounts_dim_id`),
 KEY `invoice_date_dim_id` (`invoice_date_dim_id`),
 KEY `shipment_date_dim_id` (`shipment_date_dim_id`,`client_accounts_dim_id`,`division_id`,`tracking_number`),
 CONSTRAINT `fk_invoice_details_client_accounts_dim1` FOREIGN KEY (`client_accounts_dim_id`) REFERENCES `client_accounts_dim` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB;

基本的な結合を行う最初のクエリ

SELECT count(distinct tracking_number) as val, p.division_id as division_id 
FROM client_accounts_dim c, invoices_fact p 
WHERE c.id = p.client_accounts_dim_id
AND p.division_id IN (2,3,7)
AND c.client_id = 17
AND p.shipment_date_dim_id between 20120101 and 20121108
GROUB BY p.division_id;

実行時間は 28
秒 利回りの説明

+----+-------------+-------+------+------------------------------------------------------------------+---------------------------------------------+---------+---------+------+-------------+
| id | select_type | table | type | possible_keys                                                    | key                                         | key_len | ref     | rows | Extra       |
+----+-------------+-------+------+------------------------------------------------------------------+---------------------------------------------+---------+---------+------+-------------+
|  1 | SIMPLE      | c     | ref  | PRIMARY,client_id                                                | client_id                                   | 4       | const   |   49 | Using index |
|  1 | SIMPLE      | p     | ref  | fk_package_details_client_accounts_dim1_idx,shipment_date_dim_id | fk_package_details_client_accounts_dim1_idx | 4       | c.id    |  913 | Using where |
+----+-------------+-------+------+------------------------------------------------------------------+---------------------------------------------+---------+---------+------+-------------+

最初にクエリを実行してから、client_accounts_dim_ids を入れて「手動で」結合を行うクエリ。

SELECT count(distinct tracking_number) as val, p.division_id as division_id 
FROM invoices_fact p
WHERE division_id in (2,3,7)
AND p.client_accounts_dim_id IN ( 232, 233, 234, 277, 235, 236, 279, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 278, 280, 262, 263, 264, 252, 256, 254, 259, 261, 257, 266, 276, 267, 255, 258, 274, 273, 272, 271, 269, 270, 268, 275, 253, 265, 260 )
AND p.shipment_date_dim_id between 20120101 and 20121108 
GROUP BY p.division_id;


1.6秒で実行

+----+-------------+-------+-------+------------------------------------------------------------------+------------------------+---------+------+---------+--------------------------+
| id | select_type | table | type  | possible_keys                                                    | key                    | key_len | ref  | rows    | Extra                    |
+----+-------------+-------+-------+------------------------------------------------------------------+------------------------+---------+------+---------+--------------------------+
|  1 | SIMPLE      | p     | range | fk_package_details_client_accounts_dim1_idx,shipment_date_dim_id | shipment_date_dim_id   | 19      | NULL | 4991810 | Using where; Using index |
+----+-------------+-------+-------+------------------------------------------------------------------+------------------------+---------+------+---------+--------------------------+
4

1 に答える 1

1

MySQLは実際、最初に最小のテーブルを確認する必要があります。これは-client_accounts_dimです。client_idインデックスを付けたので、情報をclient_id=17簡単に取得できます。

id次に、mysqlはを取得し、それをに結合する必要がありinvoice_factます。あなたはそれfk_invoice_details_client_accounts_dim1_idxをこのタスクのために与えました。それはすべてとても合理的に聞こえます!

さて、2つの質問、1つは難しい質問、もう1つは簡単な質問です。初め:

MySQLがインデックスでclient_accounts_dim.client_id=17の行を見つけたら、どのようにして参加する必要のあるclient_idを取得しましたか?

そして2番目:

MySQLがinvoices_fact.client_accounts_dim_idに参加すると、WHERE句の残りの情報をどのように適用しますか?

最初の質問では、InnoDBが主キーを後続のすべてのインデックスに配置することを読みましたが、結合に使用するという明確な説明に指を置くことはできません。これを明示的な複合インデックスにすることをお勧めします。

client_accounts_dim (client_id, id)

2番目の質問では、MySQLがインデックスで結合された情報を検出すると、指定した分割と日付の範囲にある行を確認するために、ディスクから適切な行をすべて読み取る必要があります。救済のための別の複合インデックス:

invoices_fact (client_accounts_dim_id, division_id, shipment_date_dim_id)

注:列2と3を正しい順序で配置し、カーディナリティの最も低い列を最初に配置します。

これで、MySQLはインデックスに出没して、行の完全なリストを収集できます。

結合について上記で説明した列を除けば、もう1つの列のみを使用しているように見えます- invoices_fact.tracking_number。これをインデックスに追加すると、MySQLは、ディスクから基になる行を読み取ることなく、クエリに必要なすべてのものをインデックスから取得できます。

invoices_fact (client_accounts_dim_id, division_id, shipment_date_dim_id, tracking_number)

注:tracking_numberは幅の広い列であり、インデックスを大きくしたり、書き込みを遅くしたり、ディスク容量を増やしたりします。両方の方法でテストできます。

お役に立てれば。

于 2012-11-11T05:35:18.503 に答える