web-dev-qa-db-ja.com

RDS上のMysqlがindex_mergeを回避

Mysqlの最適化とルーチンについてはある程度経験がありますが、最近何かに気づきました。

私はec2インスタンスでオンプレミスのmysqlインストールを使用していて、RDSに移行しただけなので、心配することなくぐっすりと眠ることができます。

問題は、私のアプリケーションにいくつかのレガシー問題があり、以前はmysql 5.1で実行されていましたが、現在はmysql 5.6で実行されていることです。

私たちのデータベースは、index_mergeがmysqlによって使用されることを前提に構築されているため、インデックスは複合ではありません。それらはすべて単一の列に影響します。これがどれほどひどいのか、どれほどひどいのかはわかっていますが、これまでのところ問題なく機能しています。テーブルが300以上あるので、今は変更できません。

私を困らせたのは、私のテーブルの1つが在庫在庫を管理していることです。テーブルの構造は次のとおりです。

id (PK)
companyId (btree index)
productId (btree index)
transactionType (input, output or stock balance; btree index)
transactionDate (btree index)
transactionPrice

本当に重要ではない他のいくつかの列。

次のように、選択クエリを実行して、特定の製品の最終価格を取得すると、

SELECT 
    transactionPrice
FROM
    stock
WHERE
    productId = x
        AND transactionType = 'input'
        AND companyId = y
        AND transactionDate < '2017-07-07'
ORDER BY transactionDate DESC
LIMIT 1;

私が期待していたのは、MySQLが非常に具体的なproductIdインデックスとcompanyIdインデックスを(ほとんどの場合)マージし、約4行を読み取ることでした。しかし実際に起こったことは、MySQLが、transactionDateをソートし、それを他のインデックスにマージしないことにより、500万行を反復することを決定したことです。

私はもう少し掘ることにしました:

  • SHOW @@optimizer_switchは、index_mergeフラグがすべてオンであることを示しています
  • オプティマイザを示唆するクエリをtransactionDateインデックスにIGNORE実行しました。 idはindex_merge productIdとcompanyIdを期待どおりに決定しました!
  • すべてのフィールドを集約したこの特定のテーブルの新しい複合インデックスを作成し、現在使用されているため、これは1つのテーブルですが他にもたくさんあるため、問題は部分的に解決されました
  • 私の最初の考えはMySQLバージョンに関連していましたが、私のノートブックはMySQL 5.7(RDSの5.6と以前のec2インスタンスの5.1ではなく)を実行しており、私のノートブックは最初のオプションとしてindex_mergeを使用しています
  • 私は走ったANALYZE TABLE、それでも同じ動作を使用します

だから今はちょっと迷ってしまいました。どの変数がこれに影響を与える可能性がありますか?フルテーブルスキャンではなく、インデックスマージの使用をオプティマイザが奨励する方法を教えてください。

編集1:リックの答えの後にさらに情報を追加する:

  • はい、テーブルは非常に大きいです-現在約1000万エントリ
  • InnoDBバッファープールのサイズは、マシンの合計メモリの3/4です。この場合、36 GBのうち25 GB(デフォルトはrds)。
  • バッファープールの使用:

BUFFER POOL AND MEMORY Total memory allocated 25738477568; in additional pool allocated 0 Dictionary memory allocated 12453955 Buffer pool size 1534976 Free buffers 8194 Database pages 1383533 Old database pages 510554 Modified db pages 4364 Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 32361311, not young 319332363 1.87 youngs/s, 0.62 non-youngs/s Pages read 15508690, created 745849, written 11868579 0.12 reads/s, 0.00 creates/s, 0.00 writes/s Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not 0 / 1000 Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: 1383533, unzip_LRU len: 0 I/O sum[24]:cur[0], unzip sum[0]:cur[0]

ご覧のとおり、バッファプールのヒットレートは1000/1000です。

一部のテーブル統計:-1000万エントリ-約75万製品-3トランザクションタイプ-約150社

最後に、作成テーブルは次のとおりです。

CREATE TABLE `estoque` (
`id` int(11) NOT NULL DEFAULT '0',
`companyId` int(11) NOT NULL DEFAULT '0',
`productId` int(11) NOT NULL DEFAULT '0',
`transactionDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`transactionType` char(1) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
KEY `transactionType` (`transactionType`),
KEY `transactionDate` (`transactionDate`),
KEY `productId` (`productId`),
KEY `companyId` (`companyId`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8

他の列もありますが、わかりやすくするために省略しました。

3
Krynble

MySQL veryがインデックスマージを使用することはほとんどありません。その理由の一部は、それが非常に非効率的であるということだと思います。それを実行するためには、

  1. 1つのインデックスの潜在的に大きな部分をスキャンし、どこかで結果を収集します。
  2. 別のインデックスの潜在的に大きな部分をスキャンし、どこかで結果を収集します。
  3. 結果をより小さな結果セットに「マージ」します(「交差」の場合、これはあなたのケースのようです)。
  4. 次に、行を調べます。

Index Mergeを使用できる場合でも、基本的には適切な複合インデックスよりも低速であることが保証されています。

問題のクエリの場合、このインデックスは最適であり、ORDER BYを処理する(それによってtmpおよびsortを回避する)こともできます。これは、Index Mergeが実行できないと思います。

INDEX(productId, transactionType, companyId,  -- in any order
      transactionDate)   -- last

関連トピックについて...これは非常に大きなテーブルだと思いますか? buffer_poolの大きさは? I/Oバウンドですか、それとも完全にキャッシュされていますか?

I/Oバウンドの場合はPRIMARY becomes important in performance. Would you care to show usSHOW CREATE TABLE`を選択するので、これらを質問します。さらに説明します。あなたが提供した情報に基づいて、クエリに必要な行はテーブル全体に散らばっていて、いくつかのブロックに「クラスター化」されていないと思います。

より役立つ情報:いくつの異なる製品? transactionTypes-どうやら3?会社?それらは均等に分散されていますか?

0
Rick James