2016-03-21 10 views
1

私はIDにプライマリキーを持つMYSQLテーブルFooと、異なるカラムに2つの他の非プライマリキーを持っています。 Fiddle "select" exampleMYSQL同等の選択クエリがない場合にテンポラリテーブルを使用してクエリを更新

実際の表には何百万行も含まれているため、説明の動作が異なります。 2つの非プライマリインデックスでIndex_Mergeを使用します。

私は、以下の説明UPDATEステートメントを実行

explain UPDATE Foo 
SET readyState = 1 
WHERE readyState = 0 
AND productName = 'OpenAM' 
LIMIT 30; 

余分な列が含まれているが、 "一時的な使用" を参照してください。

Explain Select id, productName, readyState 
FROM Foo 
WHERE readyState = 0 
AND productName = 'OpenAM' 
Limit 30; 

エクストラ列は "一時の使用" を含んでいない:

私は同等のものを実行するSELECT文を説明します。

これは私の実際のテーブルに与える影響は、私が更新すると、限界30キックが入る前に更新の条件に一致する数百万行の一時テーブルが作成されていることです。 -5秒ですが、selectはマージされたインデックスの一時テーブルを作成しないため、0.001秒しかかかりません。私は更新プログラムが3つのインデックス(プライマリ+ 2プライマリはクエリで使用されている)をすべて更新する必要があることを理解していますが、3つのインデックスで30のインデックス行を更新するのに4秒かかる場合は驚いています。

質問:強制的に更新プログラムが不要な一時テーブルを使用しないようにする方法はありますか?私は、MYSQLがSELECTクエリと同じようにUpdateクエリプランを扱ったという印象を受けました。

なぜなら、選択肢ではなく一時表が更新に必要なのはなぜですか?

EDIT:

Show Create Table (removed a heap of columns since it is a very wide table): 
    CREATE TABLE Item (
     ID int(11) NOT NULL AUTO_INCREMENT, 
     ImportId int(11) NOT NULL, 
     MerchantCategoryName varchar(200) NOT NULL, 
     HashId int(11) DEFAULT NULL, 
     Processing varchar(36) DEFAULT NULL, 
     Status int(11) NOT NULL, 
     AuditWho varchar(200) NOT NULL, 
     AuditWhen datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, 
     PRIMARY KEY (ID), 
     KEY idx_Processing_Item (Processing), 
     KEY idx_Status_Item (Status), 
     KEY idx_MerchantCategoryName_Item (MerchantCategoryName), 
     KEY fk_Import_Item (ImportId), 
     KEY fk_Hash_Item (HashId), 
     CONSTRAINT fk_Hash_Item FOREIGN KEY (HashId) REFERENCES Hash (ID), 
     CONSTRAINT fk_Import_Item FOREIGN KEY (ImportId) REFERENCES Import (ID) 
) ENGINE=InnoDB AUTO_INCREMENT=12004589 DEFAULT CHARSET=utf8 

UPDATE文

explain UPDATE Item 
    SET Processing = 'd53dbc91-eef4-11e5-a3a6-06f88beef4f3', 
     Status = 2, 
     AuditWho = 'name', 
     AuditWhen = now() 
    WHERE EventId = 1 
    AND Processing is null 
    AND Status = 1 
    LIMIT 30; 

結果:

'id','select_type','table','type','possible_keys','key','key_len','ref','rows','Extra', 
'1','SIMPLE','Item','index_merge','idx_Processing_Item,idx_Status_Item,fk_Import_Item','idx_Processing_Item,idx_Status_Item,fk_Import_Item','111,4,4',\N,'1362610','Using intersect(idx_Processing_Item,idx_Status_Item,fk_Import_Item); Using where; Using temporary', 

選択クエリ

explain select ID from Item where Status = 1 and Processing is null and ImportId = 1 limit 30; 

結果:

'id','select_type','table','type','possible_keys','key','key_len','ref','rows','Extra', 
'1','SIMPLE','Item','index_merge','idx_Processing_Item,idx_Status_Item,fk_ImportEvent_Item','idx_Processing_Item,idx_Status_Item,fk_Import_Item','111,4,4',\N,'1362610','Using intersect(idx_Processing_ItemPending,idx_Status_ItemPending,fk_ImportEvent_ItemPending); Using where; Using index', 
+0

'SHOW CREATE TABLE'と' EXPLAIN'を提供してください。 –

+0

'LIMIT'を' ORDER BY'なしで使うと、予期しない結果が出ることがあります。 –

+0

この特定のクエリでは、どの行が更新または選択されているか気にしません。私はIDで注文を説明しただけで、一時的な使用を削除しましたが、それをfilesortを使用するように変更し、今更新を実行するのにずっと時間がかかります。 –

答えて

1

推測:

UPDATEが正しい、インデックス付きの値(readyState)を変更しますか?つまり、UPDATEが使用しているので、問題のインデックスが変更されていますか?したがって、UPDATEは、行を(非効率的な方法で、明らかに)フェッチし、tmp表に投げ込んで、それからそのアクションを実行することによって、それ自体を「保護」している可能性があります。

「インデックス結合の交差」は、コンポジットインデックスよりもほとんど常に効率が悪いです(いずれの順序でも)。INDEX(readyState, productName)それを追加することをお勧めします。

ORDER BYがないため、「30」は予測できません。あなたはORDER BY the-primary-keyを追加することを提案します。

+0

これは合理的な仮定ですが、私が索引付けされていないフィールドのみを更新しますが、条件は同じです。残念ながら、まだ同じインデックスのマージを使用していて、一時テーブルを使用しています。 –

+0

他のコメントで述べたように、Order By IDを追加すると、「一時使用」を「filesortを使用」に変更したため、以前よりも実行時間が長くなります。 –

+0

これは、プライマリ以外のすべてのインデックスにインデックスを追加する提案に基づいて動作しています。私は "Use Index(compositeIndex)"を設定しなければなりませんでした。そうでなければ、まだインデックスマージを行っていました。それはまだ新しいインデックスでTemporaryを使用していることが判明しましたが、現在は非常に高速であるため、今は一時テーブルについてはそれほど重要ではありません。ありがとう –

関連する問題