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フラグがすべてオンであることを示していますIGNORE
実行しました。 idはindex_merge productIdとcompanyIdを期待どおりに決定しました!ANALYZE TABLE
、それでも同じ動作を使用しますだから今はちょっと迷ってしまいました。どの変数がこれに影響を与える可能性がありますか?フルテーブルスキャンではなく、インデックスマージの使用をオプティマイザが奨励する方法を教えてください。
編集1:リックの答えの後にさらに情報を追加する:
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
他の列もありますが、わかりやすくするために省略しました。
MySQL veryがインデックスマージを使用することはほとんどありません。その理由の一部は、それが非常に非効率的であるということだと思います。それを実行するためには、
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 us
SHOW CREATE TABLE`を選択するので、これらを質問します。さらに説明します。あなたが提供した情報に基づいて、クエリに必要な行はテーブル全体に散らばっていて、いくつかのブロックに「クラスター化」されていないと思います。
より役立つ情報:いくつの異なる製品? transactionTypes-どうやら3?会社?それらは均等に分散されていますか?